博文
C#的多线程(2)——机制探索 互斥对象——更加灵活的同步方式(2006-05-30 14:58:00)
摘要:5、互斥对象——更加灵活的同步方式
有 时候你会觉得上面介绍的方法好像不够用,对,我们解决了代码和资源的同步问题,解决了多线程自动化管理和定时触发的问题,但是如何控制多个线程相互之间的 联系呢?例如我要到餐厅吃饭,在吃饭之前我先得等待厨师把饭菜做好,之后我开始吃饭,吃完我还得付款,付款方式可以是现金,也可以是信用卡,付款之后我才 能离开。分析一下这个过程,我吃饭可以看作是主线程,厨师做饭又是一个线程,服务员用信用卡收款和收现金可以看作另外两个线程,大家可以很清楚地看到其中 的关系——我吃饭必须等待厨师做饭,然后等待两个收款线程之中任意一个的完成,然后我吃饭这个线程可以执行离开这个步骤,于是我吃饭才算结束了。事实上,现实中有着比这更复杂的联系,我们怎样才能很好地控制它们而不产生冲突和重复呢?
这种情况下,我们需要用到互斥对象,即System.Threading命名空间中的Mutex类。大家一定坐过出租车吧,事实上我们可以把Mutex看作一个出租车,那么乘客就是线程了,乘客首先得等车,然后上车,最后下车,当一个乘客在车上时,其他乘客就只有等他下车以后才可以上车。而线程与Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待Mutex对象被释放,如果它等待的Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个Mutex对象的线程都只有等待。
下面这个例子使用了Mutex对象来同步四个线程,主线程等待四个线程的结束,而这四个线程的运行又是与两个Mutex对象相关联的。其中还用到AutoResetEvent类的对象,如同上面提到的ManualResetEvent对象一样,大家可以把它简单地理解为一个信号灯,使用AutoResetEvent.Set()方法可以设置它为有信号状态,而使用AutoResetEvent.Reset()方法把它设置为无信号状态。这里用它的有信号状态来表示一个线程的结束。
// Mutex.cs
using System;
using System.Threading;
public class MutexSample
C#的多线程(2)——机制探索 线程池和定时器——多线程的自动管理 (2006-05-30 14:58:00)
摘要:四、线程池和定时器——多线程的自动管理
在多线程的程序中,经常会出现两种情况。一种情况下,应用程序中的线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应;而另外一种情况则是线程平常都处于休眠状态,只是周期性地被唤醒。在.net framework里边,我们使用ThreadPool来对付第一种情况,使用Timer来对付第二种情况。
ThreadPool类提供一个由系统维护的线程池——可以看作一个线程的容器,该容器需要Windows 2000以上版本的系统支持,因为其中某些方法调用了只有高版本的Windows才有的API函数。你可以使用ThreadPool.QueueUserWorkItem()方法将线程安放在线程池里,该方法的原型如下:
//将一个线程放进线程池,该线程的Start()方法将调用WaitCallback代理对象代表的函数
public static bool QueueUserWorkItem(WaitCallback);
//重载的方法如下,参数object将传递给WaitCallback所代表的方法
public static bool QueueUserWorkItem(WaitCallback, object);
要注意的是,ThreadPool类也是一个静态类,你不能也不必要生成它的对象,而且一旦使用该方法在线程池中添加了一个项目,那么该项目将是没有办法取消的。在这里你无需自己建立线程,只需把你要做的工作写成函数,然后作为参数传递给ThreadPool.QueueUserWorkItem()方法就行了,传递的方法就是依靠WaitCallback代理对象,而线程的建立、管理、运行等等工作都是由系统自动完成的,你无须考虑那些复杂的细节问题,线程池的优点也就在这里体现出来了,就好像你是公司老板——只需要安排工作,而不必亲自动手。
下面的例程演示了ThreadPool的用法。首先程序创建了一个ManualResetEvent对象,该对象就像一个信号灯,可以利用它的信号来通知其它线程,本例中当线程池中所有线程工作都完成以后,ManualResetEvent的对象将被设置为有信号,从而通知主线程继续运行。它有几个重要的方法:Rese......
C#的多线程(2)——机制探索 线程的同步和通讯——生产者和消费者 (2006-05-30 14:56:00)
摘要:三.线程的同步和通讯——生产者和消费者
假 设这样一种情况,两个线程同时维护一个队列,如果一个线程对队列中添加元素,而另外一个线程从队列中取用元素,那么我们称添加元素的线程为生产者,称取用 元素的线程为消费者。生产者与消费者问题看起来很简单,但是却是多线程应用中一个必须解决的问题,它涉及到线程之间的同步和通讯问题。
前面说过,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。但是多线程环境下,可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在C#中,关键字lock定义如下:
lock(expression) statement_block
expression代表你希望跟踪的对象,通常是对象引用。一般地,如果你想保护一个类的实例,你可以使用this;如果你希望保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。而statement_block就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。
下面是一个使用lock关键字的典型例子,我将在注释里向大家说明lock关键字的用法和用途:
//lock.cs
using System;
using System.Threading;
internal class Account
{
int balance;
Random r = new Random();
internal Account(int initial)
{
balance = initial;
}
internal int Withdraw(int amount)
{
if (balance < 0)
{
file://如果balance小于0则抛出异常
......
C#的多线程(2)——机制探索 操纵一个线程(2006-05-30 14:55:00)
摘要:二.操纵一个线程
任何程序在执行时,至少有一个主线程,下面这段小程序可以给读者一个直观的印象:
//SystemThread.cs
using System;
using System.Threading;
namespace ThreadTest
{
class RunIt
{
[STAThread]
static void Main(string[] args)
{
Thread.CurrentThread.Name="System Thread";//给当前线程起名为"System Thread"
Console.WriteLine(Thread.CurrentThread.Name+"''''Status:"+Thread.CurrentThread.ThreadState);
Console.ReadLine();
}
}
}
编译执行后你看到了什么?是的,程序将产生如下输出:
System Thread''''s Status:Running
在这里,我们通过Thread类的静态属性CurrentThread获取了当前执行的线程,对其Name属性赋值“System Thread”,最后还输出了它的当前状态(ThreadState)。所谓静态属性,就是这个类所有对象所公有的属性,不管你创建了多少个这个类的实例,但是类的静态属性在内存中只有一个。很容易理解CurrentThread为什么是静态的——虽然有多个线程同时存在,但是在某一个时刻,CPU只能执行其中一个。
就像上面程序所演示的,我们通过Thread类来创建和控制线程。注意到程序的头部,我们使用了如下命名空间:
using System;
using System.Threading;
在.net framework class library中,所有与多线程机制应用相关的类都是放在System.Threading命名空间中的。其中提供Thread类用于创建线程,ThreadP......
C#的多线程(2)——机制探索 多线程的概念(2006-05-30 14:54:00)
摘要:注:本文中出现的代码均在.net Framework RC3环境中运行通过
一.多线程的概念
Windows是一个多任务的系统,如果你使用的是windows 2000及 其以上版本,你可以通过任务管理器查看当前系统运行的程序和进程。什么是进程呢?当一个程序开始运行时,它就是一个进程,进程所指包括运行中的程序和程序 所使用到的内存和系统资源。而一个进程又是由多个线程所组成的,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。浏览器就是一个很好的多线程的例子,在浏览器中你可以在下载JAVA小应用程序或图象的同时滚动页面,在访问新页面时,播放动画和声音,打印文件等。
多线程的好处在于可以提高CPU的利用率——任何一个程序员都不希望自己的程序很多时候没事可干,在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。
然而我们也必须认识到线程本身可能影响系统性能的不利方面,以正确使用线程:
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多
多线程需要协调和管理,所以需要CPU时间跟踪线程
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题
线程太多会导致控制太复杂,最终可能造成很多Bug
基 于以上认识,我们可以一个比喻来加深理解。假设有一个公司,公司里有很多各司其职的职员,那么我们可以认为这个正常运作的公司就是一个进程,而公司里的职 员就是线程。一个公司至少得有一个职员吧,同理,一个进程至少包含一个线程。在公司里,你可以一个职员干所有的事,但是效率很显然是高不起来的,一个人的 公司也不可能做大;一个程序中也可以只用一个线程去做事,事实上,一些过时的语言如fortune,basic都是如此,但是象一个人的公司一样,效率很低,如果做大程序,效率更低——事实上现在几乎没有单线程的商业软件。公司的职员越多,老板就得发越多的薪水给他们,还得耗费大量精力去管理他们,协调他们之间的矛盾和......
使用 Mutex 对象提供对资源的独占访问(MSDN)(2006-05-30 13:22:00)
摘要:可以使用 Mutex 对象提供对资源的独占访问。Mutex 类比 Monitor 类使用更多系统资源,但是它可以跨应用程序域边界进行封送处理,可用于多个等待,并且可用于同步不同进程中的线程。有关托管同步机制的比较,请参见同步基元概述。
有关代码示例,请参见 Mutex 构造函数的参考文档。
使用 Mutex
线程调用 mutex 的 WaitOne 方法请求所有权。该调用会一直阻塞到 mutex 可用,或直至达到可选的超时间隔。如果没有任何线程拥有它,则 Mutex 的状态为已发信号的状态。
线程通过调用其 ReleaseMutex 方法释放 mutex。mutex 具有线程关联;即 mutex 只能由拥有它的线程释放。如果线程释放不是它拥有的 mutex,则会在该线程中引发 ApplicationException。
由于 Mutex 类从 WaitHandle 派生,所以您还可以结合其他等待句柄调用 WaitHandle 的静态 WaitAll 或 WaitAny 方法请求 Mutex 的所有权。
如果某个线程拥有 Mutex,则该线程就可以在重复的等待-请求调用中指定同一个 Mutex,而不必阻止其执行;但是,它必须释放 Mutex,次数与释放所属权的次数相同。
被放弃的 mutex
如果线程终止而未释放 Mutex,则认为该 mutex 已放弃。这是严重的编程错误,因为该 mutex 正在保护的资源可能会处于不一致的状态。在 .NET Framework 2.0 版中,获取该 mutex 的下一个线程中将引发 AbandonedMutexException。
注意
在 .NET Framework 1.0 和 1.1 版中,放弃的 Mutex 被设置为已发送信号状态,下一个等待线程将获得所有权。如果没有等待线程,则 Mutex 保持已发送信号状态。不引发异常。
本地 mutex 和系统 mutex
Mutex 分两种类型:本地 mutex 和命名系统 mutex。如果使用接受名称的构造函数创建了 Mutex 对象,那么该对象将与具有该名称的操作系统对象相关联。命名的系统 mutex 在整个操作系统中都可见,并且可用于同步进程活动。您可以创建多个 Mutex 对象来表示同一命名系统 mutex,而......
如何在程序中互斥的操作数据库 (2006-05-30 13:21:00)
摘要:估计大多数人在写数据库相关的程序的时候,都是用一个数据库Connection。因此有时会遇到在程序并发操作数据库所造成的异常问题。解决这类问题,很多人都是在建立一个数据库连接,来分别操作;但是如果并发的数据库操作不知两个的时候,那前面所说得的方法就显得不是很实际。
其实,在程序中用一个数据库Connection很容易解决对数据库的互斥操作,大致的方法如下:
1. 首先,需要建立一个全局静态的互斥量;例如:
using System.Threading;
private static Mutex m;
2. 修改原先操作数据库的部分,注意的是(数据库查询是不需要的):
if(m==null)
m=new Mutex();
//Enter into mutex area
m.WaitOne();
//Execute sql command here
//Depart from mutex area
m.......
多线程编程技术 (2006-05-26 14:21:00)
摘要:传统的Visual Basic开发人员已经建立了同步应用程序,在这些程序中事务按顺序执行。尽管由于多个事务多多少少地同时运行使多线程应用程序效率更高,但是使用先前版本的Visual Basic很难建立这类程序。
多线程程序是可行的,因为操作系统是多任务的,它有模拟同一时刻运行多个应用程序的能力。尽管多数个人计算机只有一个处理器,但是现在的操作系统还是通过在多个执行代码片断之间划分处理器时间提供了多任务。线程可能是整个应用程序,但通常是应用程序可以单独运行的一个部分。操作系统根据线程的优先级和离最近运行的时间长短给每一个线程分配处理时间。多线程对于时间密集型事务(例如文件输入输出)应用程序的性能有很大的提高。
但是也有必须细心的地方。尽管多线程能提高性能,但是每个线程还是需要用附加的内存来建立和处理器时间来运行,建立太多的线程可能降低应用程序的性能。当设计多线程应用程序时,应该比较性能与开销。
多任务成为操作系统的一部分已经很久了。但是直到最近Visual Basic程序员才能使用无文档记录特性(undocumented)或者间接使用COM组件或者操作系统的异步部分执行多线程事务。.NET框架组件为开发多线程应用程序,在System.Threading名字空间中提供了全面的支持。
本文讨论多线程的好处以及怎样使用Visual Basic .NET开发多线程应用程序。尽管Visual Basic .NET和.NET框架组件使开发多线程应用程序更容易,但是本文作了调整使其适合高级读者和希望从早期Visual Basic转移到Visual Basic .NET的开发人员。
多线程处理的优点
尽管同步应用程序易于开发,但是它们的性能通常比多线程应用程序低,因为一个新的事务必须等待前面的事务完成后才能开始。如果完成某个同步事务的时间比预想的要长,应用程序可能没有响应。多线程处理可以同时运行多个过程。例如,字处理程序能够在继续操作文档的同时执行拼写检查事务。因为多线程应用程序把程序分解为独立的事务,它们能通过下面的途径充分提高性能:
l 多线程技术可以使程序更容易响应,因为在其它工作继续时用户界面可以保持激活。
l 当前不忙的......
C#电子书下载地址(顶)(2006-05-26 13:37:00)
摘要:1、http://www.codepub.com/software/View-Software-2497.html
2、http://www.codepub.com/index.html......
关于使用线程始终检索数据库问题? (2006-05-26 13:18:00)
摘要:大家好:
现在我想实现在打开应用程序主窗体时用线程来运行一个方法。。该方法就是不停的检索远程服务器,如果满足条件的则触发另外一个方法。。该功能类似于移动公司不停的在检索数据库,然后发送短信一样。
该检索从运行应用程序到应用程序关闭。。大家有什么好的方法吗
你可以写成线程类,然后把窗体的方法委托给线程即可
Sample code as follows:
public delegate void InvokeFun( string sData );
public class clsThreadFun
{
private InvokeFun myFunHandler;
private yourForm pParent;
public clsThreadFun( yourForm pMain, InvokeFun FunHandler )
{
pParent = pMain;
myFunHandler = FunHandler;
}
public void ThreadFun()
{
//Do what you want here
pParent.InvokeMethod( this.myFunHandler, new object[]{"Test"} );
}
}
----Start su......