参考2、
线程间的通讯机制。
总的说来一共有三种方法: 使用全局变量 使用Windows消息传递机制 使用事件 上面我们说了线程会共享进程的资源,其中全局变量也包括在内,所以线程可以通过使用全局变量来通讯。但是这种办法的明显的缺点是在有多个线程存取同一个全局变量时,必须考虑同步的问题。譬如:有一个有十个成员变量的结构体,其中一个线程在对起赋值时,假设只更新了五个成员变量的值,这时内核的调度线程剥夺其运行权给另一个线程,这样接下来的线程如果想要用该全局结构体变量,它的值就显然不对了。另外多线程的程序也很难调试,尤其这些错误很隐蔽和很难复现时。如果两个线程都是用户界面线程时,用WINDOWS的消息机制来进行线程间的通讯是比较方便的. 您所要做的只是自定义一些windows消息(注意不要和windows的预定义的消息冲突),然后在线程之间传递可以了。您可以这样来定义消息,把WM_USER(它的值等于0x0400)当作基数,然后顺序地去加序号,譬如: WM_MYCUSTOMMSG equ WM_USER+100h 小于WM_USER 的值是Windows系统的保留值,大于该值留给用户来使用。 如果其中有一个线程是工作者线程的话,那就不能用该种方法来进行通讯了,这是因为工作者线程没有消息队列。您应当用下面这种策略来进行工作者线程和用户界面线程之间的通讯: User interface Thread ------> global variable(s)----> Worker thread Worker Thread ------> custom window message(s) ----> User interface Thread 稍后我们的例子中将讲解这种通讯办法。 最后的办法是事件对象。您可以把事件对象看作是一种标志。如果事件对象的状态是无信号的话,说明该线程正在睡眠或挂起,在该种状态下系统是不会给该线程分配CPU时间片的。当一个线程的状态转成有信号时,WINDOWS就会唤醒该线程并且让它正常运行。 例子: 您可以下载例子并运行thread1.exe,然后激活菜单项"Savage Calculation",然后程序开始执行指令"add eax,eax ",一共执行600,000,000次,您会发现在这个过程当中,用户界面将停止响应,您既不能使用菜单,也不能使用移动窗口。等到计算完成后,会弹出一个对话框,关闭掉对话框后窗口才可以和当初一样正常运行了。 为了避免这种不便,我们把计算的工作放入到一个单独的工作者线程中去,而主窗口仅仅响应用户的活动。您可以看到虽然用户界面的反应比平常时慢了,但还是可以工作的。
参考3、
前台线程和后台线程
Net的公用语言运行时(Common Language Runtime,CLR)能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。
一个线程是前台线程还是后台线程可由它的IsBackground属性来决定。这个属性是可读又可写的。它的默认值为false,即意味着一个线程默认为前台线程。我们可以将它的IsBackground属性设置为true,从而使之成为一个后台线程。
下面的例子是一个控制台程序,程序一开始便启动了10个线程,每个线程运行5秒钟时间。由于线程的IsBackground属性默认为false,即它们都是前台线程,所以尽管程序的主线程很快就运行结束了,但程序要到所有已启动的线程都运行完毕才会结束。示例代码如下:
using System;
using System.Threading;
class MyApp
{
public staticvoid Main()
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(newThreadStart(ThreadFunc));
thread.Start();
}
}
private staticvoid ThreadFunc()
{
DateTime start = DateTime.Now;
while ((DateTime.Now - start).Seconds < 5)
;
}
}
接下来我们对上面的代码进行略微修改,将每个线程的IsBackground属性都设置为true,则每个线程都是后台线程了。那么只要程序的主线程结束了,整个程序也就结束了。示例代码如下:
using System;
using System.Threading;
class MyApp
{
public staticvoid Main()
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(newThreadStart(ThreadFunc));
thread.IsBackground = true;
thread.Start();
}
}
private staticvoid ThreadFunc()
{
DateTime start = DateTime.Now;
while ((DateTime.Now - start).Seconds < 5)
;
}
}
既然前台线程和后台线程有这种差别,那么我们怎么知道该如何设置一个线程的IsBackground属性呢?下面是一些基本的原则:对于一些在后台运行的线程,当程序结束时这些线程没有必要继续运行了,那么这些线程就应该设置为后台线程。比如一个程序启动了一个进行大量运算的线程,可是只要程序一旦结束,那个线程就失去了继续存在的意义,那么那个线程就该是作为后台线程的。而对于一些服务于用户界面的线程往往是要设置为前台线程的,因为即使程序的主线程结束了,其他的用户界面的线程很可能要继续存在来显示相关的信息,所以不能立即终止它们。这里我只是给出了一些原则,具体到实际的运用往往需要编程者的进一步仔细斟酌。