C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[5]: プロパティをボラタイル(マルチスレッド対応)できませんか?


(過去ログ 138 を表示中)

[トピック内 20 記事 (1 - 20 表示)]  << 0 >>

■81114 / inTopicNo.1)  プロパティをボラタイル(マルチスレッド対応)できませんか?
  
□投稿者/ パラオ (1回)-(2016/08/30(Tue) 13:30:22)

分類:[C#] 

VS2015, C#6.0でコーディングしています。

プロパティをvolatileにできないという仕様について,代替策はあるのでしょうか?


これはOKです。

public volatile bool Hoge;


これはだめです。コンパイルできません。

public volatile bool Hoge { get; set; };


Hogeは複数のスレッドからアクセスされます。


プロパティをvolatileにできない理由について,
ある程度調べてはみたものの,
どうもvolatileそのものについての理解が足りていないのか,
なぜこうできないのかわからなかったもので・・・。

プロパティの実態は,getterやsetterなどのメソッドなので
フィールドと同じ扱いをすることはできないのだ
という事のようですが,
それだとマルチスレッドでプロパティを使うなということなのでしょうか。

プロパティをvolatileにできないならば,
じゃあ代わりにどういう手段をとればいいのか
という情報が見つかりませんでした。


「プロパティはvolatileにできないので,かわりに○○する」

皆さんからお知恵を拝借できれば幸いです。
引用返信 編集キー/
■81115 / inTopicNo.2)  Re[1]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ 774RR (447回)-(2016/08/30(Tue) 14:16:00)
プロパティにアクセスするということは C/C++ でいうところのメンバ関数を呼ぶということなので
プロパティ自体を volatile にすることに意味は無いっす。

プロパティの中で使っているメンバ変数を volatile にするのは意味があると思う。

private volatile bool internal_hoge;
public bool Hoge
{
    get { return internal_hoge; }
    set { internal_hoge=value; }
}
# 排他してないし限りなく意味がないけど

volatile は万能ではないので「あらゆる状況」に対応できるものではないということは頭において欲しい。

引用返信 編集キー/
■81125 / inTopicNo.3)  Re[1]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ Jitta (204回)-(2016/08/30(Tue) 15:57:46)
No81114 (パラオ さん) に返信

そもそもの話をするなら、
揮発性だとマークする=常に最新の情報が入っている
という理解が間違いかと。
「複数のスレッドから変更される可能性がある」=コンパイル時の最適化で消えたり配置換えされたりしない
じゃないかな。

とすると、メソッドにvolatileをマークする意味がないですよね。
引用返信 編集キー/
■81132 / inTopicNo.4)  Re[2]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ ?p???I (1回)-(2016/08/30(Tue) 17:01:37)
774RRさん,ありがとうございます。

・どうしてもvolatileにしたければ,プロパティの内部変数をvolatileにすることは可能。

・でも,それだけやっても,排他にはならない。

という事なんですね。う〜ん。。


No81115 (774RR さん) に返信に意味は無いっす。
>
> プロパティの中で使っているメンバ変数を volatile にするのは意味があると思う。
>

引用返信 編集キー/
■81133 / inTopicNo.5)  Re[3]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ ito (29回)-(2016/08/30(Tue) 17:17:13)
No81132 (?p???I さん) に返信
具体的に何をしたいのかわかりませんが、こんな感じではだめなのでしょうか!?

private object hoge_lock = new object();
private volatile bool hoge_value = false;

public bool Hoge
{
    get { return hoge_value; }
    set
    {
        lock (hoge_lock)
        {
            return hoge_value;
        }
    }
}

引用返信 編集キー/
■81134 / inTopicNo.6)  Re[2]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ PANG2 (144回)-(2016/08/30(Tue) 17:26:33)
そもそも

public bool Hoge { get; set; }

で、何が問題なのでしょうか?
引用返信 編集キー/
■81137 / inTopicNo.7)  Re[3]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ 魔界の仮面弁士 (853回)-(2016/08/30(Tue) 19:19:40)
No81132 (?p???I さん) に返信

パラオさんですね。

掲示板の cookie 処理の関係上、一部のブラウザーでは
二回目以降の投稿者名が化けてしまうようです。
(Chrome や IE なら恐らく大丈夫ですが、ローカルプロキシを使っていると化ける事も…)


> プロパティの内部変数を

プロパティ値を格納するための private なフィールド変数のことを
バッキングフィールド(backing field)と呼びます。
https://msdn.microsoft.com/ja-jp/library/ms173118.aspx


> ・でも,それだけやっても,排他にはならない。

どこからどこまでを排他にしたいのでしょうか。

Hoge プロパティの役目を明確にしていただいた方が、
どういう実装にするべきかという案を出しやすいです。



単純に値の読み書きだけを問題にするなら、bool な Hoge プロパティの
バッキングフィールドを int 型にしておいて、setter では
その変数の操作に、System.Threading.Interlocked クラスの
CompareExchange や Exchange メソッドを使うことで対処できます。

ただ、そうして作った Hoge プロパティや、あるいは最初に書かれていた
「public volatile bool Hoge;」であったとしても、複数のスレッドから
 Hoge = !Hoge;
のように単純に操作されてしまうと、スレッドセーフな操作にはなりません。

スレッドセーフにしたいのであれば、特定のスレッドが値を変更している最中は、
他のいずれのスレッドからも読み書きされないことを保証した設計が必要になってきます。
(たとえば ReaderWriterLockSlim クラスを使うなど)



C# 2005 世代のマルチスレッド処理で使われる
「BackgroundWorker クラス」を一例として挙げてみますと、このクラスの
CancellationPending プロパティは readOnly となっていますよね。

このため、外部からはこの値を『直接的に』変更することができません。

CancelAsync メソッドを使うことで、間接的に変更させることはできますが、
このプロパティの値を変化させられるのは、BackgroundWorker 自身だけであり、
複数のスレッドから同時に変更されることが無いというわけです。
引用返信 編集キー/
■81138 / inTopicNo.8)  Re[2]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ パラオ (2回)-(2016/08/30(Tue) 19:25:55)
Jittaさん,ありがとうございます。

volatileについてよくわかっていないままの質問で申し訳ないです。
少しイメージがわいた気がするのですが・・・


・volatileは「コンパイル時に最適化で消さないでね。」という指示のしるし。
・フィールドは「コンパイル時に最適化で消される」可能性がある。だからvolatileを付与しうる。
・メソッドは「コンパイル時に最適化で消される」可能性はない。だからvolatileを付与できない。
・プロパティはメソッド(getter + setter)である。だからvolatileにしようがないし,する必要もない。

という理解なのかな,と思いました。



No81125 (Jitta さん) に返信
> ■No81114 (パラオ さん) に返信
> 「複数のスレッドから変更される可能性がある」=コンパイル時の最適化で消えたり配置換えされたりしない

引用返信 編集キー/
■81139 / inTopicNo.9)  Re[4]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ パラオ (3回)-(2016/08/30(Tue) 19:42:58)
ito様,PANG2様,魔界の仮面弁士様
詳しい解説をありがとうございます。

私はプロキシを使っていませんが,
Windows10+Firefox(43.0)でこの掲示板を閲覧しております。
引用返信をすると,名前欄が文字化けするようですね。


私がしたいこと,プロパティ化の目的
言及しておらずすみません。最初に書くべきですよね!

私がしたいことは,別の人が作った既存のC#コードを引継がれて
自分は機能改修を行なっている,
という状況なのです。


引き継いだコードの中身は
 public volatile bool Hoge;
となっていました。
このフィールドの役目としては,
あるスレッドが作業完了したかどうかを別スレッドで参照するための判定フラグでした。

私が引き継いだのち,Hogeをプロパティ化したい(setter処理内にいろいろ記述したい)
という要望が生まれたのです。

そのため,
「もともとvolatileが付いていたフィールドを,リファクタリングでプロパティ化する時に,
もともと付いていたvolatileというキーワードを削除してしまって大丈夫だろうか??」
という疑問が生まれました。


>どこからどこまでを排他にしたいのでしょうか。

↑このご質問に答えるとしたら,
「既存のコードのマルチスレッドの挙動に手を加えることなく,今まで通りの挙動を保ちたい。」
という回答になるかと思います…。

できるだけ既存のコードからの変更を減らしたいので,
volatileキーワードも消さずに残しておいたほうが良いのだろうか?
という考えでした。



>最初に書かれていた
>「public volatile bool Hoge;」であったとしても、・・・スレッドセーフな操作にはなりません。

そうだったのですね!!
ということは,引き継いだコードの設計ミスだと思います。


>スレッドセーフにしたいのであれば、特定のスレッドが値を変更している最中は、
>他のいずれのスレッドからも読み書きされないことを保証した設計が必要になってきます。
>(たとえば ReaderWriterLockSlim クラスを使うなど)

私の役目は,「このフラグをプロパティ化し,なおかつスレッドセーフにしたい」ところです。

今のままのコードではダメみたいですね。
私も,引き継ぎ元のコードを書いた人も,volatileの理解が足りておりませんでした。
引用返信 編集キー/
■81141 / inTopicNo.10)  Re[4]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ 魔界の仮面弁士 (855回)-(2016/08/30(Tue) 20:19:07)
No81133 (ito さん) に補足
> 具体的に何をしたいのかわかりませんが、こんな感じではだめなのでしょうか!?

> set
> {
>   lock (hoge_lock)
>   {
>     return hoge_value;
>   }
> }

「return hoge_value;」ではなく、「hoge_value = value;」ですね。

この場合、Hoge プロパティの setter が同時に操作されることは無くなります。
引用返信 編集キー/
■81142 / inTopicNo.11)  Re[3]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ Jitta (206回)-(2016/08/30(Tue) 20:41:57)
No81138 (パラオ さん) に返信
> ・volatileは「コンパイル時に最適化で消さないでね。」という指示のしるし。
> ・フィールドは「コンパイル時に最適化で消される」可能性がある。だからvolatileを付与しうる。
> ・メソッドは「コンパイル時に最適化で消される」可能性はない。だからvolatileを付与できない。
> ・プロパティはメソッド(getter + setter)である。だからvolatileにしようがないし,する必要もない。
> 
> という理解なのかな,と思いました。

Visual Studio をインストールしたディレクトリ\VC#\Specifications\1041
に、CSharp Language Specification.docx という仕様書があります。
私はVS12 のを参照しますが、10.5.3章から引用します。

> 非 volatile フィールドの場合は、命令を並べ替える最適化手法を使うと、lock-statement (8.12 を参照) によって
> 提供されるような同期化を行わずにフィールドにアクセスするマルチスレッド プログラムで、予期しない結果が生じ
> ることがあります。これらの最適化を実行できるのは、コンパイラ、実行時システム、またはハードウェアです。
> volatile フィールドの場合、並べ替えによる最適化は次のように制限されます。> 
> using System;
> using System.Threading;
> class Test
> {
> 	public static int result;   
> 	public static volatile bool finished;
> 	static void Thread2() {
> 		result = 143;    
> 		finished = true; 
> 	}
> 	static void Main() {
> 		finished = false;
> 		// Run Thread2() in a new thread
> 		new Thread(new ThreadStart(Thread2)).Start();
> 		// Wait for Thread2 to signal that it has a result by setting
> 		// finished to true.
> 		for (;;) {
> 			if (finished) {
> 				Console.WriteLine("result = {0}", result);
> 				return;
> 			}
> 		}
> 	}
> }

この例では、Thread2 メソッドの順番が問題になります。
result への代入が先に実行されると、"result = 143" という結果になります。
result への代入が後になると、"result = 0" になります。
result も finished も、Thread2 メソッドの中では代入でしか使われていないので、
このメソッドしか見なければ、実行の順番はどうでもよい、はずです。

「最適化で消される」のは、ローカル変数で、参照されない場合、ですね。
ほかのものと混ざったようです。失礼しました。

引用返信 編集キー/
■81143 / inTopicNo.12)  Re[5]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ 魔界の仮面弁士 (856回)-(2016/08/30(Tue) 21:28:27)
No81139 (パラオ さん) に返信
> Windows10+Firefox(43.0)でこの掲示板を閲覧しております。

FireFox は、バージョン問わず化けると思います。

掲示板管理者の方であれば、cookie の読み書き部分に手を加える事で
化けなくなるすることができるはずですが、そうすると
以前から使っていた人達の cookie が腐るというジレンマ。


> 既存のC#コードを引継がれて
「C#」ではなく、
「C#」が正式名称だったりします。細かい事ですが豆知識として。


[JIS X 3015]
》 C#は,“しーしゃーぷ”と発音する。
》 C#は,LATIN CAPITAL LETTER C (U+0043)の次に NUMBER SIGN # (U+0023)を書く。

[ECMA-334]
》 The name C# is pronounced “C Sharp”.
》 The name C# is written as the LATIN CAPITAL LETTER C (U+0043) followed by the NUMBER SIGN # (U+0023).




―――閑話休題。


> 私が引き継いだのち,Hogeをプロパティ化したい(setter処理内にいろいろ記述したい)
> という要望が生まれたのです。
単純な置き換えなら No81133 (と No81141)のコードも一案です。

この場合、そのプロパティ以外から、バッキングフィールドを
絶対に操作しないということが前提になります。


> >「public volatile bool Hoge;」であったとしても、・・・スレッドセーフな操作にはなりません。

volatile がマズイと言っているワケでは無いですよ。念のため。


> そうだったのですね!!

例に挙げた『Hoge = !Hoge;』がマズイという理由は、それが
  「Hoge値の読み込み」→「反転」→「Hoge値の書き込み」
という動作であったためです。

『Hoge = !Hoge;』を 2 回行うと、元の値に戻るわけですが、
それはあくまでも、手順が分離されない「直列操作」の場合。

「並列操作」の場合、2 つのスレッドが同時に古い値を読み込むと、
2 回実施されたのに、値が反転されたままにもなりえるわけで。

しかもシビアなタイミングの問題なので、デバッグで追えるものでもないという。


> ということは,引き継いだコードの設計ミスだと思います。

必ずしも間違いであると決め付ける事が出来ないのが難しいところでして。
スレッドセーフである事が、他の場所で保証されている可能性もありえますし。

いずれにせよ、特定の一部だけを見るのではなく、クラス全体として
スレッドセーフな設計を意識する必要があるでしょう。


> 私の役目は,「このフラグをプロパティ化し,なおかつスレッドセーフにしたい」ところです。

たとえば、int 型の変数があって、それを +1 する場合、複数のスレッドから
 this.intValue++;
とするのさえ NG です。理由は先の『Hoge = !Hoge;』と同じ。

なので、こうした一連の書き込み処理が分断されないような手続きが必要になります。

具体的には lock ブロックで囲むとか、あるいは
 System.Threading.Interlocked.Increment(ref this.intValue);
に書き変えるとか、多重編集時は例外を投げるなど。

たとえば仮に lock するとしたら、
 ・どこからどこまでを lock するのか?
 ・lock している間、他のスレッドは「待ち」になるが大丈夫か?
 ・lock し忘れて書き変えると、スレッドセーフにならないが、漏れは無いか?
といった点を気に掛ける必要があるでしょう。

パフォーマンスが問題になる場合は、ReaderWriterLockSlim のように
読み込みロックと書き込みロックが分離されたものを使うこともあります。
引用返信 編集キー/
■81147 / inTopicNo.13)  Re[3]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ パラオ (4回)-(2016/08/30(Tue) 23:41:57)
皆さま,ご教授ありがとうございます。とても親切ですね。

volatileの意味,
かわりにどの構文やクラスを使うべきか,
マルチスレッドの基礎,
設計の見直し
などについて,よくわかりました。

volatileについての
このスレッドはいったん解決済みとします。

(再設計,再実装ののちにまた疑問点が生じたら
別スレッドにて投稿するかもしれませんが…。)

ありがとうございました!!
解決済み
引用返信 編集キー/
■81151 / inTopicNo.14)  Re[5]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ ito (30回)-(2016/08/31(Wed) 09:39:14)
No81141 (魔界の仮面弁士 さん) に返信
> ■No81133 (ito さん) に補足
>>具体的に何をしたいのかわかりませんが、こんな感じではだめなのでしょうか!?
>
>> set
>> {
>>   lock (hoge_lock)
>>   {
>>     return hoge_value;
>>   }
>> }
>
> 「return hoge_value;」ではなく、「hoge_value = value;」ですね。
その通りです。
適当に書いたのがばればれですね(汗


引用返信 編集キー/
■81152 / inTopicNo.15)  Re[4]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ なちゃ (140回)-(2016/08/31(Wed) 10:02:16)
No81147 (パラオ さん) に返信
> 皆さま,ご教授ありがとうございます。とても親切ですね。
>
> volatileの意味,
> かわりにどの構文やクラスを使うべきか,
> マルチスレッドの基礎,
> 設計の見直し
> などについて,よくわかりました。
>
> volatileについての
> このスレッドはいったん解決済みとします。
>
> (再設計,再実装ののちにまた疑問点が生じたら
> 別スレッドにて投稿するかもしれませんが…。)
>
> ありがとうございました!!

一応コードの書き換えの観点で。
処理完了フラグとして単純なvolatileフィールドを使っていて、set時にいろいろしたくなったのなら、バッキングフィールドをvolatileにして書き込みはlockで囲むとかでだいたい大丈夫なことが多いでしょう。
いろいろやる内容によっては何ともいえませんが。

書き込みが単一スレッドからのみと保証されてるなら、volatileだけでも大丈夫でしょうけど、volatileフィールドへの書き込みは普通は最後に行う必要があります。

あと、昔のc言語などでのvolatileとは違い、今時のvolatileはスレッド間でのメモリの可視可に絡む役割が与えられてます。
※むしろ最近はこっちがとくに重要

なので、プロパティにvolatileは意味がないとは一概にはいえなくて(何しろ書き込みと読みとりというセマンティックスを持っているので)、volatile的なメモリバリア機能を持たせることには意味がある可能性はあります。
もちろん現在の言語仕様ではそういう機能はありませんが。

解決済み
引用返信 編集キー/
■81153 / inTopicNo.16)  Re[5]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ なちゃ (141回)-(2016/08/31(Wed) 10:08:13)
例えばvolatileプロパティは、getの最初とsetの最後で自動でメモリフェンスが実行される、みたいな仕様があっても、おかしくはないということです。

そこまで簡易的に表に出すべきとは思えないのでそんな言語仕様は採用されないでしょうけども。

解決済み
引用返信 編集キー/
■81158 / inTopicNo.17)  Re[3]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ PANG2 (145回)-(2016/08/31(Wed) 10:28:09)
No81134 (PANG2 さん) に返信
> そもそも
> 
> public bool Hoge { get; set; }
> 
> で、何が問題なのでしょうか?


私なりに考えると問題は、

・setterの二重侵入で終了処理が複数走る可能性がある
・Hogeをtrueからfalseに変える機能は不要

ということで


private object hoge_lock = new object();
private volatile bool hoge_value = false;

public bool Hoge
{
	get { return hoge_value; }
	
	// false→false : 何もしない
	// false→true  : 終了処理(二重侵入をロック)
	// true→true   : なにもしない
	// true→false  : 例外
	set
	{
		lock (hoge_lock) {
			if (value == hoge_value) {
				return;
			} else if (value) {
				hoge_value = true;
				//終了処理
			} else {
				throw new Exception("already finished");
			}
		}
	}
}

解決済み
引用返信 編集キー/
■81167 / inTopicNo.18)  Re[4]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ なちゃ (143回)-(2016/08/31(Wed) 12:21:06)
No81158 (PANG2 さん) に返信
> ■No81134 (PANG2 さん) に返信
>>そもそも
>>
>>public bool Hoge { get; set; }
>>
>>で、何が問題なのでしょうか?
>
>
> 私なりに考えると問題は、
>
> ・setterの二重侵入で終了処理が複数走る可能性がある
> ・Hogeをtrueからfalseに変える機能は不要
>
> ということで
>
>
> private object hoge_lock = new object();
> private volatile bool hoge_value = false;
>
> public bool Hoge
> {
> get { return hoge_value; }
>
> // false→false : 何もしない
> // false→true : 終了処理(二重侵入をロック)
> // true→true : なにもしない
> // true→false : 例外
> set
> {
> lock (hoge_lock) {
> if (value == hoge_value) {
> return;
> } else if (value) {
> hoge_value = true;
> //終了処理
> } else {
> throw new Exception("already finished");
> }
> }
> }
> }

フィールドへの代入は終了処理の後にするのが常道かなと思います。
そもそも何をやるかによるので絶対ではありませんが、まあ一般論とか普通はということで。
※フィールドへの代入が先にあると、終了処理が終わっていない段階で、読みとり側で終了フラグが完了と判断される可能性があります。
解決済み
引用返信 編集キー/
■81169 / inTopicNo.19)  Re[6]: プロパティをボラタイル(マルチスレッド対応)できませんか?
□投稿者/ なちゃ (144回)-(2016/08/31(Wed) 12:28:38)
No81153 (なちゃ さん) に返信
> 例えばvolatileプロパティは、getの最初とsetの最後で自動でメモリフェンスが実行される、みたいな仕様があっても、おかしくはないということです。
>
> そこまで簡易的に表に出すべきとは思えないのでそんな言語仕様は採用されないでしょうけども。

ぼけてたのでこれだけ訂正。
メモリフェンスの場所が逆でした。

getの最後とsetの最初でメモリフェンス実行ですね。
何でこの順にする必要があるかは、スレッド間でのメモリ可視可による順序づけの問題から来ています。


解決済み
引用返信 編集キー/
■81171 / inTopicNo.20)  Re[5]: プロパティをボラタイル(マルチスレッド対応)できませんか?
 
□投稿者/ PANG2 (146回)-(2016/08/31(Wed) 12:36:33)
2016/08/31(Wed) 12:42:55 編集(投稿者)

No81167 (なちゃ さん) に返信
> フィールドへの代入は終了処理の後にするのが常道かなと思います。
> そもそも何をやるかによるので絶対ではありませんが、まあ一般論とか普通はということで。
> ※フィールドへの代入が先にあると、終了処理が終わっていない段階で、読みとり側で終了フラグが完了と判断される可能性があります。

なるほど。その方がよいですね。
ご指摘ありがとうございます。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -