如何在不同的執行緒中安全地修改 Window Form Control
問題出處:Windows Workflow Foundation 新一代工作流程開發實務 Page 23 範例
由於工作流程與介面控制項是執行在不同的執行緒上,因此當工作流程完成後要將結果顯示在 Window Form Control 時,必須透過代理 (delegate) 的方式存取,但此程式卻會造成當機,詳細請參考後面程式的說明
而解決方法也相當簡單,只要在執行 this.Invoke(...) 之前把主執行緒叫醒就可以了,也就是將程式改成下面的方式就萬事 OK 了
由於工作流程與介面控制項是執行在不同的執行緒上,因此當工作流程完成後要將結果顯示在 Window Form Control 時,必須透過代理 (delegate) 的方式存取,但此程式卻會造成當機,詳細請參考後面程式的說明
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Threading;
using FirstWF;
namespace WindowClient
{
public partial class StartFlow : Form
{
public StartFlow()
{
InitializeComponent();
}
// 利用 Event 提供事件處理程式完成後,返回主程式的機制
private AutoResetEvent WaitHandle = new AutoResetEvent(false);
delegate void SetTextCallback(string Message);
private void Button1_Click(object sender, EventArgs e)
{
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
// 設定 Workflow 的事件處理常式
workflowRuntime.WorkflowCompleted += new EventHandler(OnWorkflowCompleted);
workflowRuntime.WorkflowTerminated += new EventHandler(OnWorkflowTerminated);
int FirstNumber, SecondNumber;
FirstNumber = int.Parse(txtFirstNumber.Text);
SecondNumber = int.Parse(txtSecondNumber.Text);
// 將取得的參數放入 Dictionary 中傳入工作流程
Dictionary parameters = new Dictionary();
parameters.Add("FirstNumber", FirstNumber);
parameters.Add("SecondNumber", SecondNumber);
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(FirstWF.GetMax), parameters);
instance.Start();
// 啟動 Workflow 後,主執行緒即進入 Suspend 模式等待
WaitHandle.WaitOne();
}
}
private void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
SetText(e.Exception.Message);
WaitHandle.Set();
}
private void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
// Workflow 完成後,顯示結果
SetText(String.Format("較大的數字為:{0}", e.OutputParameters["Result"]));
WaitHandle.Set();
}
private void SetText(string Message)
{
// 檢查是否在不同的執行緒
if (this.lblMessage.InvokeRequired)
{
// 設定新的代理程式
SetTextCallback d = new SetTextCallback(SetText);
// 將程式丟到主執行緒的 Queue 等待執行
this.Invoke(d, new object[] { Message });
}
else
{
this.lblMessage.Text = Message;
}
}
}
}
看到問題了嗎?當在 SetText 中執行 this.Invoke(...) 時,會等到主執行緒執行完代理的 SetText 才會返回。但問題是此時主執行緒已進入 Suspend 的模式,因此就造成 Deadlock 當掉了。而解決方法也相當簡單,只要在執行 this.Invoke(...) 之前把主執行緒叫醒就可以了,也就是將程式改成下面的方式就萬事 OK 了
private void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
WaitHandle.Set();
SetText(e.Exception.Message);
}
private void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
WaitHandle.Set();
SetText(String.Format("較大的數字為:{0}", e.OutputParameters["Result"]));
}
留言