by:Alex Fr 译:adaiye
简介
.NET框架提供很多方法来实现多线程编程。我想展示如何通过同步访问用户接口去运行一个工作线程(例如,一个读取记录集并要在一个窗体中填充控件的线程)。运行线程,我用:
- 线程实例和主线程函数
- 两个使线程停止的事件。第一个事件是设置何时主线程想停止工作线程,第二个事件是工作线程设置何时想真正停止。
System.Windows.Forms.Control的函数。要想从另一个线程中访问
System.Windows.Forms.Control中的函数,你需要使用
Control.Invoke(同步调用)或
Control.BeginInvoke(异步调用)函数。比如说显示数据库的记录,我们就需要
Invoke
。要想实现这个,我们要用:
- 一个为了访问窗体的委托类型。委托实例和函数调用使用这个委托。
- 从工作线程来的函数调用。
- 启动“停止线程”事件;
- 等待“线程已经停止”事件;
- 用
Application.DoEvents函数等待事件处理消息。这是为了防止死锁的,因为工作线程的
Invoke调用是被主线程所处理的。
Demo有两个类:
MainForm和
LongProcess
。LongProcess.Run函数在线程中被执行,它会用几行数据来填充列表。工作线程可能会自然停止,或者当用户按下
"Stop Thread"按钮或关闭窗体时停止。源代码
1 // MainForm.cs
2
3 namespace WorkerThread
4 {
5 // delegates used to call MainForm functions from worker thread
6 public delegate void DelegateAddString(String s);
7 public delegate void DelegateThreadFinished();
8
9 public class MainForm : System.Windows.Forms.Form
10 {
11 //
12
13 // worker thread
14 Thread m_WorkerThread;
15
16 // events used to stop worker thread
17 ManualResetEvent m_EventStopThread;
18 ManualResetEvent m_EventThreadStopped;
19
20 // Delegate instances used to call user interface functions
21 // from worker thread:
22 public DelegateAddString m_DelegateAddString;
23 public DelegateThreadFinished m_DelegateThreadFinished;
24
25 //
26
27 public MainForm()
28 {
29 InitializeComponent();
30
31 // initialize delegates
32 m_DelegateAddString = new DelegateAddString(this.AddString);
33 m_DelegateThreadFinished = new DelegateThreadFinished(this.ThreadFinished);
34
35 // initialize events
36 m_EventStopThread = new ManualResetEvent(false);
37 m_EventThreadStopped = new ManualResetEvent(false);
38
39 }
40
41 //
42
43 // Start thread button is pressed
44 private void btnStartThread_Click(object sender, System.EventArgs e)
45 {
46 //
47
48 // reset events
49 m_EventStopThread.Reset();
50 m_EventThreadStopped.Reset();
51
52 // create worker thread instance
53 m_WorkerThread = new Thread(new ThreadStart(this.WorkerThreadFunction));
54
55 m_WorkerThread.Name = "Worker Thread Sample"; // looks nice in Output window
56
57 m_WorkerThread.Start();
58
59 }
60
61
62 // Worker thread function.
63 // Called indirectly from btnStartThread_Click
64 private void WorkerThreadFunction()
65 {
66 LongProcess longProcess;
67
68 longProcess = new LongProcess(m_EventStopThread, m_EventThreadStopped, this);
69
70 longProcess.Run();
71 }
72
73 // Stop worker thread if it is running.
74 // Called when user presses Stop button or form is closed.
75 private void StopThread()
76 {
77 if ( m_WorkerThread != null && m_WorkerThread.IsAlive ) // thread is active
78 {
79 // set event "Stop"
80 m_EventStopThread.Set();
81
82 // wait when thread will stop or finish
83 while (m_WorkerThread.IsAlive)
84 {
85 // We cannot use here infinite wait because our thread
86 // makes syncronous calls to main form, this will cause deadlock.
87 // Instead of this we wait for event some appropriate time
88 // (and by the way give time to worker thread) and
89 // process events. These events may contain Invoke calls.
90 if ( WaitHandle.WaitAll(
91 (new ManualResetEvent[] {m_EventThreadStopped}),
92 100,
93 true) )
94 {
95 break;
96 }
97
98 Application.DoEvents();
99 }
100 }
101 }
102
103 // Add string to list box.
104 // Called from worker thread using delegate and Control.Invoke
105 private void AddString(String s)
106 {
107 listBox1.Items.Add(s);
108 }
109
110 // Set initial state of controls.
111 // Called from worker thread using delegate and Control.Invoke
112 private void ThreadFinished()
113 {
114 btnStartThread.Enabled = true;
115 btnStopThread.Enabled = false;
116 }
117
118 }
119 }
120
121 // LongProcess.cs
122
123 namespace WorkerThread
124 {
125 public class LongProcess
126 {
127 //
128
129 // Function runs in worker thread and emulates long process.
130 public void Run()
131 {
132 int i;
133 String s;
134
135 for (i = 1; i <= 10; i++)
136 {
137 // make step
138 s = "Step number " + i.ToString() + " executed";
139
140 Thread.Sleep(400);
141
142 // Make synchronous call to main form.
143 // MainForm.AddString function runs in main thread.
144 // (To make asynchronous call use BeginInvoke)
145 m_form.Invoke(m_form.m_DelegateAddString, new Object[] {s});
146
147
148 // check if thread is cancelled
149 if ( m_EventStop.WaitOne(0, true) )
150 {
151 // clean-up operations may be placed here
152 //
153
154 // inform main thread that this thread stopped
155 m_EventStopped.Set();
156
157 return;
158 }
159 }
160
161 // Make synchronous call to main form
162 // to inform it that thread finished
163 m_form.Invoke(m_form.m_DelegateThreadFinished, null);
164 }
165
166 }
167 }
168
169
170
171
172
2
3 namespace WorkerThread
4 {
5 // delegates used to call MainForm functions from worker thread
6 public delegate void DelegateAddString(String s);
7 public delegate void DelegateThreadFinished();
8
9 public class MainForm : System.Windows.Forms.Form
10 {
11 //
12
13 // worker thread
14 Thread m_WorkerThread;
15
16 // events used to stop worker thread
17 ManualResetEvent m_EventStopThread;
18 ManualResetEvent m_EventThreadStopped;
19
20 // Delegate instances used to call user interface functions
21 // from worker thread:
22 public DelegateAddString m_DelegateAddString;
23 public DelegateThreadFinished m_DelegateThreadFinished;
24
25 //
26
27 public MainForm()
28 {
29 InitializeComponent();
30
31 // initialize delegates
32 m_DelegateAddString = new DelegateAddString(this.AddString);
33 m_DelegateThreadFinished = new DelegateThreadFinished(this.ThreadFinished);
34
35 // initialize events
36 m_EventStopThread = new ManualResetEvent(false);
37 m_EventThreadStopped = new ManualResetEvent(false);
38
39 }
40
41 //
42
43 // Start thread button is pressed
44 private void btnStartThread_Click(object sender, System.EventArgs e)
45 {
46 //
47
48 // reset events
49 m_EventStopThread.Reset();
50 m_EventThreadStopped.Reset();
51
52 // create worker thread instance
53 m_WorkerThread = new Thread(new ThreadStart(this.WorkerThreadFunction));
54
55 m_WorkerThread.Name = "Worker Thread Sample"; // looks nice in Output window
56
57 m_WorkerThread.Start();
58
59 }
60
61
62 // Worker thread function.
63 // Called indirectly from btnStartThread_Click
64 private void WorkerThreadFunction()
65 {
66 LongProcess longProcess;
67
68 longProcess = new LongProcess(m_EventStopThread, m_EventThreadStopped, this);
69
70 longProcess.Run();
71 }
72
73 // Stop worker thread if it is running.
74 // Called when user presses Stop button or form is closed.
75 private void StopThread()
76 {
77 if ( m_WorkerThread != null && m_WorkerThread.IsAlive ) // thread is active
78 {
79 // set event "Stop"
80 m_EventStopThread.Set();
81
82 // wait when thread will stop or finish
83 while (m_WorkerThread.IsAlive)
84 {
85 // We cannot use here infinite wait because our thread
86 // makes syncronous calls to main form, this will cause deadlock.
87 // Instead of this we wait for event some appropriate time
88 // (and by the way give time to worker thread) and
89 // process events. These events may contain Invoke calls.
90 if ( WaitHandle.WaitAll(
91 (new ManualResetEvent[] {m_EventThreadStopped}),
92 100,
93 true) )
94 {
95 break;
96 }
97
98 Application.DoEvents();
99 }
100 }
101 }
102
103 // Add string to list box.
104 // Called from worker thread using delegate and Control.Invoke
105 private void AddString(String s)
106 {
107 listBox1.Items.Add(s);
108 }
109
110 // Set initial state of controls.
111 // Called from worker thread using delegate and Control.Invoke
112 private void ThreadFinished()
113 {
114 btnStartThread.Enabled = true;
115 btnStopThread.Enabled = false;
116 }
117
118 }
119 }
120
121 // LongProcess.cs
122
123 namespace WorkerThread
124 {
125 public class LongProcess
126 {
127 //
128
129 // Function runs in worker thread and emulates long process.
130 public void Run()
131 {
132 int i;
133 String s;
134
135 for (i = 1; i <= 10; i++)
136 {
137 // make step
138 s = "Step number " + i.ToString() + " executed";
139
140 Thread.Sleep(400);
141
142 // Make synchronous call to main form.
143 // MainForm.AddString function runs in main thread.
144 // (To make asynchronous call use BeginInvoke)
145 m_form.Invoke(m_form.m_DelegateAddString, new Object[] {s});
146
147
148 // check if thread is cancelled
149 if ( m_EventStop.WaitOne(0, true) )
150 {
151 // clean-up operations may be placed here
152 //
153
154 // inform main thread that this thread stopped
155 m_EventStopped.Set();
156
157 return;
158 }
159 }
160
161 // Make synchronous call to main form
162 // to inform it that thread finished
163 m_form.Invoke(m_form.m_DelegateThreadFinished, null);
164 }
165
166 }
167 }
168
169
170
171
172