UIスレッドで使用する場合

以下のコードがUIスレッド上から呼び出されると、worker.DoWorkはUIスレッドとは別スレッドで動作する。
一方、worker.RunWorkerCompletedとProgressChanged はUIスレッド(workerを作ったスレッド)で実行される。
WPFはUIスレッドでしかコントロールのプロパティを変更できないので、RunWorkerCompletedとProgressChangedをうまく使うと楽できそうだ。

		private void Button_Click(object sender, RoutedEventArgs e) {
			Console.WriteLine("ButtonClick: " + Thread.CurrentThread.ManagedThreadId);
			var worker = new BackgroundWorker();
			worker.WorkerReportsProgress = true;
			worker.DoWork += (s, arg) => {
				for (int i = 0; i < 3; i++) {
					Console.WriteLine(string.Format("DoWork{0}: {1}",
						i, Thread.CurrentThread.ManagedThreadId));
					worker.ReportProgress(i);
				}
			};
			worker.ProgressChanged += (s, arg) => {
				Console.WriteLine(string.Format("ProgressChanged{0}: {1}" , arg.ProgressPercentage,
					Thread.CurrentThread.ManagedThreadId));
			};
			worker.RunWorkerCompleted += (s, arg) => {
				Console.WriteLine("Completed: " + Thread.CurrentThread.ManagedThreadId);
			};
			worker.RunWorkerAsync();
		}
出力は
ButtonClick: 10
DoWork0: 7
DoWork1: 7
DoWork2: 7
ProgressChanged0: 10
ProgressChanged1: 10
ProgressChanged2: 10
Completed: 10

挙動を確認するための実験

サンプルコード

	public partial class App : Application {
		private class MyWorker : BackgroundWorker {
			private string _name;
			public MyWorker(string name) {
				_name = name;
			}

			protected override void OnDoWork(DoWorkEventArgs e) {
				Debug.WriteLine(string.Format("Worker {0} DoWork called", _name));
				System.Threading.Thread.Sleep(100);
				Debug.WriteLine(string.Format("Worker {0} DoWork finished", _name));
			}

			protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e) {
				Debug.WriteLine(string.Format("Worker {0} WorkerCompleted called", _name));
				System.Threading.Thread.Sleep(100);
				Debug.WriteLine(string.Format("Worker {0} WorkerCompleted finished", _name));
			}
		}

		private BackgroundWorker[] _workers;

		protected override void OnStartup(StartupEventArgs e) {
			base.OnStartup(e);

			_workers = new BackgroundWorker[10];
			for (int i = 0; i < 10; i++) {
				_workers[i] = new MyWorker(i.ToString());
			}

			foreach (var w in _workers)
				w.RunWorkerAsync();

			System.Console.ReadLine();
		}
	}

結果

Worker 0 DoWork called
Worker 1 DoWork called
Worker 2 DoWork called
Worker 3 DoWork called
Worker 0 DoWork finished
Worker 1 DoWork finished
Worker 2 DoWork finished
Worker 5 DoWork called
Worker 4 DoWork called
Worker 6 DoWork called
Worker 3 DoWork finished
Worker 7 DoWork called
Worker 0 WorkerCompleted called
Worker 5 DoWork finished
Worker 7 DoWork finished
Worker 4 DoWork finished
Worker 6 DoWork finished
Worker 8 DoWork called
Worker 9 DoWork called
Worker 0 WorkerCompleted finished
Worker 1 WorkerCompleted called
Worker 8 DoWork finished
Worker 9 DoWork finished
Worker 1 WorkerCompleted finished
Worker 2 WorkerCompleted called
Worker 2 WorkerCompleted finished
Worker 3 WorkerCompleted called
Worker 3 WorkerCompleted finished
Worker 5 WorkerCompleted called
Worker 5 WorkerCompleted finished
Worker 7 WorkerCompleted called
Worker 7 WorkerCompleted finished
Worker 4 WorkerCompleted called
Worker 4 WorkerCompleted finished
Worker 6 WorkerCompleted called
Worker 6 WorkerCompleted finished
Worker 8 WorkerCompleted called
Worker 8 WorkerCompleted finished
Worker 9 WorkerCompleted called
Worker 9 WorkerCompleted finished

この結果からわかること

  • RunWorkerAsync()を読んだ順番に実行されるとは限らない
  • DoWorkは並列で実行されるが、WorkerCompletedは並列で実行されない(ほかが完全に終了してから)
  • つまり、WorkerCompletedはSleepするとUIスレッド自体をSleepさせるのでやってはいけない。(意図的にやるのならいいかもしれない)

要するに、以下のようなコードを書いてはダメだと言うこと(汗
			AutoResetEvent ev = new AutoResetEvent(false);
			BackgroundWorker worker = new BackgroundWorker();
			worker.DoWork += delegate {
				//時間のかかる処理
			};
			worker.RunWorkerCompleted += delegate {
				ev.Set();
			};
			worker.RunWorkerAsync();
			ev.WaitOne();
			//workerが終了してから、これ以降の処理を継続したい
ほかのスレッドで重い処理をしてから、完了後続きの処理をやろうとして書いたコードだけど、*1
別にWaitOne()しているあいだディスパッチャがUIスレッドで別の処理をやってくれる訳じゃない。
RunWorkerCompletedが呼ばれるのは、その外のスレッドと同じ。
つまりWaitOne()で止まっているスレッドなので、RunWorkerCompletedは永遠に呼ばれることはない。

ちゃんとわかっていればあたりまえかもしれないけど、
うっかりしててはまったので一応メモとして書いておきます。

このページへのコメント

i4kmFD <a href="http://zpyjtbngbfwk.com/">zpyjtbngbfwk</a>, [url=http://uyyyvntrlxsb.com/]uyyyvntrlxsb[/url], [link=http://pwzkjtbpjuon.com/]pwzkjtbpjuon[/link], http://aiqbdpbhrbsj.com/

0
Posted by drqxwqmi 2013年11月14日(木) 12:17:29 返信

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

どなたでも編集できます