はじめに
C#でTaskを使用した非同期処理をする場合に複数スレッド間でメッセージのやりとりをする方法をまとめます。
実行環境
- Windows 7 Professional
- Visual Studio Professional 2017
事前準備
MVVM Light ToolkitのMessengerを使用するのでNuGetでインストールします。
プロキシの設定が必要な場合はNuGet.Configに次の記述を追加します。
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="http_proxy" value="http://~~" /> <add key="http_proxy.user" value="user_name" /> </packageSources> </configuration>
サンプルプログラムの構成
ここではTaskA
とTaskB
が並列実行している中で、TaskA
がとあるイベントを契機にTaskB
にメッセージを送信し、TaskB
はメッセージを処理し終えたことをTaskA
に通知するプログラムを想定します。
実装
まずはじめにProgram::Main()
で2つのTask
をrun()
します。
class Program { static void Main(string[] args) { TaskA taskA = new TaskA(); TaskB taskB = new TaskB(); var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var tasks = new[] { Task.Run(() => taskA.run(token), token), Task.Run(() => taskB.run(token), token) }; Task.WaitAll(tasks); } }
続けてTask
側ですが、各Task
のコンストラクタではメッセージ受信時に起動したいメソッドをMessenger.Default.Register
で登録しています。
TaskA
はResponseMessage
型のインスタンスがMessenger.Default.Send
されたときに、TaskA::onReceivedMessage(ResponseMessage res)
を実行します。
TaskB
はRequestMessage
型のインスタンスがMessenger.Default.Send
されたときに、TaskB::onReceivedMessage(RequestMessage req)
を実行します。
Messenger.Default.Send
はメッセージをMessenger
に登録している全タスクにブロードキャストしてしまうので、メッセージの型を変えることでメッセージの送信先を制御しています。
class TaskA { public TaskA() { Messenger.Default.Register<ResponseMessage>(this, onReceivedMessage); } public void run(CancellationToken token) { // do something RequestMessage req = new RequestMessage(); req.msg = "TaskA's message."; Messenger.Default.Send<RequestMessage>(req); } private void onReceivedMessage(ResponseMessage res) { Console.WriteLine("TaskB sent the following message : " + res.msg); } } class TaskB { public TaskB() { Messenger.Default.Register<RequestMessage>(this, onReceivedMessage); } public void run(CancellationToken token) { //do something } private void onReceivedMessage(RequestMessage req) { Console.WriteLine("TaskA sent the following message : " + req.msg); ResponseMessage res = new ResponseMessage(); res.msg = "TaskB's message."; Messenger.Default.Send(res); } } class RequestMessage { public string msg { get; set; } } class ResponseMessage { public string msg { get; set; } }
さいごに
これでひとまずやりたいことはできましたが、より良い方法が見つかったら追記します。