转帖地址:http://blog.csdn.net/Knight94/archive/2006/08/24/1111267.aspx
C#
是一门支持多线程的语言,因此线程的使用也是比较常见的。由于线程的知识在
Win32
编程的时候已经说得过多,所以在
.Net
中很少介绍这部分(可能
.Net
不觉得这部分是它所特有的)。
那么线程相关的问题大致有如下四类(这篇文章只讨论单线程、单线程与
UI
线程这两方面的问题)。
问题一,线程的基本操作,例如:暂停、继续、停止等;
问题二,如何向线程传递参数或者从中得到其返回值;
问题三,如何使线程所占用的
CPU
不要老是百分之百;
最后一个,也是问题最多的,就是如何在子线程来控制
UI
中的控件,换句话说,就是在线程中控制窗体某些控件的显示。
对于问题一,我不建议使用
Thread
类提供的
Suspend
、
Resume
以及
Abort
这三个方法,前两个有问题,好像在
VS05
已经屏蔽这两个方法;对于
Abort
来说,除了资源没有得到及时释放外,有时候会出现异常。
如何做呢,通过设置开关变量来完成。
对于问题二,我不建议使用静态成员来完成,仅仅为了线程而破坏类的封装有些得不偿失。
那如何做呢,通过创建单独的线程类来完成。
对于问题三来说,造成这个原因是由于线程中进行不间断的循环操作,从而使
CPU
完全被子线程占有。那么处理此类问题,其实很简单,在适当的位置调用
Thread.Sleep(20)
来释放所占有
CPU
资源,不要小看这
20
毫秒的睡眠,它的作用可是巨大的,可以使其他线程得到
CPU
资源,从而使你的
CPU
使用效率降下来。
看完前面的三个问题的解释,对于如何做似乎没有给出一个明确的答案,为了更好地说明如何解决这三个问题,我用一个比较完整的例子展现给大家,代码如下。
//--------------------------- Sub-thread class
---------------------------------------
//------------------------------------------------------------------------------------
//---File:
clsSubThread
//---Description:
The
sub-thread template class file
//---Author:
Knight
//---Date:
Aug.21,
2006
//------------------------------------------------------------------------------------
//---------------------------{Sub-thread
class}---------------------------------------
namespace
ThreadTemplate
{
using
System;
using
System.Threading;
using
System.IO;
///
<summary>
///
Summary description
for clsSubThread.
///
</summary>
public
class
clsSubThread:IDisposable
{
private
Thread thdSubThread = null
;
private
Mutex mUnique = new
Mutex();
private
bool
blnIsStopped;
private
bool
blnSuspended;
private
bool
blnStarted;
private
int
nStartNum;
public
bool
IsStopped
{
get
{ return
blnIsStopped; }
}
public
bool
IsSuspended
{
get
{ return
blnSuspended; }
}
public
int
ReturnValue
{
get
{ return
nStartNum;}
}
public
clsSubThread( int
StartNum )
{
//
// TODO: Add constructor logic
here
//
blnIsStopped =
true
;
blnSuspended =
false
;
blnStarted = false
;
nStartNum =
StartNum;
}
///
<summary>
///
Start
sub-thread
///
</summary>
public
void
Start()
{
if
( !blnStarted )
{
thdSubThread =
new
Thread( new
ThreadStart( SubThread ) );
blnIsStopped =
false
;
blnStarted =
true
;
thdSubThread.Start();
}
}
///
<summary>
///
Thread entry
function
///
</summary>
private
void
SubThread()
{
do
{
// Wait for resume-command if got suspend-command here
mUnique.WaitOne();
mUnique.ReleaseMutex();
nStartNum++;
Thread.Sleep(1000); // Release CPU
here
}while
( blnIsStopped == false
);
}
///
<summary>
///
Suspend
sub-thread
///
</summary>
public
void
Suspend()
{
if
( blnStarted && !blnSuspended
)
{
blnSuspended =
true
;
mUnique.WaitOne();
}
}
///
<summary>
///
Resume
sub-thread
///
</summary>
public
void
Resume()
{
if
( blnStarted && blnSuspended
)
{
blnSuspended =
false
;
mUnique.ReleaseMutex();
}
}
///
<summary>
///
Stop
sub-thread
///
</summary>
public
void
Stop()
{
if
( blnStarted )
{
if
( blnSuspended )
Resume();
blnStarted =
false
;
blnIsStopped =
true
;
thdSubThread.Join();
}
}
#region
IDisposable Members
///
<summary>
///
Class resources
dispose here
///
</summary>
public
void
Dispose()
{
// TODO:
Add
clsSubThread.Dispose implementation
Stop();//Stop thread first
GC.SuppressFinalize( this
);
}
#endregion
}
}
那么对于调用呢,就非常简单了,如下:
// Create new sub-thread object with
parameters
clsSubThread
mySubThread = new
clsSubThread( 5
);
mySubThread.Start();//Start
thread
Thread.Sleep( 2000
);
mySubThread.Suspend();//Suspend thread
Thread.Sleep( 2000
);
mySubThread.Resume();//Resume thread
Thread.Sleep( 2000
);
mySubThread.Stop();//Stop thread
//Get thread's return value
Debug.WriteLine(
mySubThread.ReturnValue );
//Release sub-thread object
mySubThread.Dispose();
在回过头来看看前面所说的三个问题。
对于问题一来说,首先需要局部成员的支持,那么
private
Mutex mUnique = new
Mutex();
private
bool
blnIsStopped;
private
bool
blnSuspended;
private
bool
blnStarted;
光看成员名称,估计大家都已经猜出其代表的意思。接下来需要修改线程入口函数,要是这些开关变量能发挥作用,那么看看
SubThread
这个函数。
///
<summary>
///
Thread entry
function
///
</summary>
private
void
SubThread()
{
do
{
// Wait for resume-command if got suspend-command here
mUnique.WaitOne();
mUnique.ReleaseMutex();
nStartNum++;
Thread.Sleep(1000);
}while
( blnIsStopped == false
);
}
函数比较简单,不到十句,可能对于“
blnIsStopped == false
”这个判断来说,大家还比较好理解,这是一个普通的判断,如果当前
Stop
开关打开了,就停止循环;否则一直循环。
大家比较迷惑的可能是如下这两句:
mUnique.WaitOne();
mUnique.ReleaseMutex();
这两句的目的是为了使线程在
Suspend
操作的时候能发挥效果,为了解释这两句,需要结合
Suspend
和
Resume
这两个方法,它俩的代码如下。
///
<summary>
///
Suspend
sub-thread
///
</summary>
public
void
Suspend()
{
if
( blnStarted && !blnSuspended
)
{
blnSuspended =
true
;
mUnique.WaitOne();
}
}
///
<summary>
///
Resume
sub-thread
///
</summary>
public
void
Resume()
{
if
( blnStarted && blnSuspended
)
{
blnSuspended =
false
;
mUnique.ReleaseMutex();
}
}
为了更好地说明,还需要先简单说说
Mutex
类型。对于此类型对象,当调用对象的
WaitOne
之后,如果此时没有其他线程对它使用的时候,就立刻获得信号量,继续执行代码;当再调用
ReleaseMutex
之前,如果再调用对象的
WaitOne
方法,就会一直等待,直到获得信号量的调用
ReleaseMutex
来进行释放。这就好比卫生间的使用,如果没有人使用则可以直接使用,否则只有等待。
明白了这一点后,再来解释这两句所能出现的现象。
mUnique.WaitOne();
mUnique.ReleaseMutex();
当在线程函数中,执行到“
mUnique.WaitOne();
”这一句的时候,如果此时外界没有发送
Suspend
消息,也就是信号量没有被占用,那么这一句可以立刻返回。那么为什么要紧接着释放呢,因为不能总占着信号量,立即释放信号量是避免在发送
Suspend
命令的时候出现等待;如果此时外界已经发送了
Suspend
消息,也就是说信号量已经被占用,此时“
mUnique.WaitOne();
”不能立刻返回,需要等到信号量被释放才能继续进行,也就是需要调用
Resume
的时候,“
mUnique.WaitOne();
”才能获得信号量进行继续执行。这样才能达到真正意义上的
Suspend
和
Resume
。
至于线程的
Start