c# 进程之间的线程同步
Mutex类、Event类、SemaphoreSlim类和ReaderWriterLockSlim类等提供了多个进程之间的线程同步。
1、WaitHandle基类
WaitHandle抽象类,用于等待一个信号的设置。可以根据其派生类的不同,等待不同的信号。异步委托的BeginInvoke()方法返回一个实现了IAsycResult接口的对象。使用IAsycResult接口可以用AsycWaitHandle属性访问WaitHandle基类。在调用WaitOne()方法时,线程会等待接收一个和等待句柄相关的信号:
staticvoidMain(string[]args) { Funcfunc=newFunc ( ()=> { Thread.Sleep(1500); return1; }); IAsyncResultar=func.BeginInvoke(null,null); intcount=0; while(true) { Interlocked.Increment(refcount); Console.WriteLine("第{0}周期循环等待结果。",count); if(ar.AsyncWaitHandle.WaitOne(100,false)) { Console.WriteLine("获得返回结果。"); break; } } intresult=func.EndInvoke(ar); Console.WriteLine("结果为:{0}",result); }
使用WaitHandle基类可以等待一个信号的出现(WaitHandle()方法)、等待多个对象都必须发出信号(WaitAll()方法)、等待多个对象中任一一个发出信号(WaitAny()方法)。其中WaitAll()方法和WaitAny()方法时WaitHandle类的静态方法,接收一个WaitHandle参数数组。
WaitHandle基类的SafeWaitHandle属性,其中可以将一个本机句柄赋予一个系统资源,等待该句柄,如I/O操作,或者自定义的句柄。
2、Mutex类
Mutex类继承自WaitHandle类,提供跨多个进程同步访问的一个类。类似于Monitor类,只能有一个线程拥有锁定。在Mutex类的构造函数各参数含义:
- initiallyOwned:如果为true,则给予调用线程已命名的系统互斥体的初始所属权(如果已命名的系统互斥体是通过此调用创建的);否则为false。
- name:系统互斥体的名称。如果值为null,则System.Threading.Mutex是未命名的。
- createdNew:在此方法返回时,如果创建了局部互斥体(即,如果name为null或空字符串)或指定的命名系统互斥体,则包含布尔值true;如果指定的命名系统互斥体已存在,则为false。该参数未经初始化即被传递。
- mutexSecurity:一个System.Security.AccessControl.MutexSecurity对象,表示应用于已命名的系统互斥体的访问控制安全性。
互斥也可以在另一个进程中定义,操作系统能够识别有名称的互斥,它由进程之间共享。如果没有指定互斥的名称,则不在不同的进程之间共享。该方法可以检测程序是否已运行,可以禁止程序启动两次。
staticvoidMain(string[]args) { //ThreadingTimer(); //TimersTimer(); boolisCreateNew=false; Mutexmutex=newMutex(false,"MyApp",outisCreateNew);//查询是否已有互斥“MyApp”存在 if(isCreateNew==false) { //已存在互斥 } }
要打开已有互斥,可以使用Mutex.OpenExisting()方法,不需要构造函数创建互斥时需要的相同.Net权限。可以使用WaitOne()方法获得互斥的锁定,成为该互斥的拥有着。调用ReleaseMutex()方法释放互斥:
if(mutex.WaitOne())//设置互斥锁定 { try { //执行代码 } finally{ mutex.ReleaseMutex();//释放互斥 } } else { //出现问题 }
3、Semaphore类
信号量是一种计数的互斥锁定,可以同时由多个线程使用。信号量可定义允许同时访问受旗语锁定保护的资源的线程个数。Semaphore和SemaphoreSlim两个类具有信号量功能。Semaphore类可以指定名称,让其在系统资源范围内查找到,允许在不同的进程之间同步。Semaphore类是对较短等待时间进行优化了的轻型版本。
staticvoidMain() { inttaskCount=6; intsemaphoreCount=3; Semaphoresemaphore=newSemaphore(semaphoreCount,semaphoreCount,"Test");//创建计数为3的信号量 /*第一个参数为初始释放的锁定数,第二个参数为可锁定的总数。如果第一个参数小于第二个参数,其差值就是已分配线程的计量数。 *第三个参数为信号指定的名称,能让它在不同的进程之间共享。 */ vartasks=newTask[taskCount]; for(inti=0;iTaskMain(semaphore));//创建6个任务 } Task.WaitAll(tasks); Console.WriteLine("Alltasksfinished"); } //锁定信号的任务 staticvoidTaskMain(Semaphoresemaphore) { boolisCompleted=false; while(!isCompleted)//循环等待被释放的信号量 { if(semaphore.WaitOne(600))//最长等待600ms { try { Console.WriteLine("Task{0}locksthesemaphore",Task.CurrentId); Thread.Sleep(2000);//2s后释放信号 } finally { Console.WriteLine("Task{0}releasesthesemaphore",Task.CurrentId); semaphore.Release();//释放信号量 isCompleted=true; } } else { //超过规定的等待时间,写入一条超时等待的信息 Console.WriteLine("Timeoutfortask{0};waitagain",Task.CurrentId); } } }
以上方法中,信号量计数为3,因此最多只有三个任务可获得锁定,第4个及以后的任务必须等待。在解除锁定时,任何情况下一定要解除资源的锁定。
4、Events类
事件也是一个系统范围内资源同步的方法。主要由以下几个类提供:ManualResetEvent、AutoResetEvent、ManualResetEventSlim、和CountdownEvent类。
ManualResetEventSlim类中,调用Set()方法可以发出信号;调用Reset()方法可以使重置为无信号状态。如果多个线程在等待向一个事件发出信号,并调用Set()方法,就释放所有等待线程。如果一个线程刚刚调用了WiatOne()方法,但事件已发出信号,等待的线程就可以继续等待。
AutoResetEvent类中,同样可以通过Set()方法发出信号、Reset()方法重置信号,但是该类是自动重置信号。如果一个线程在等待自动重置的事件发信号,当第一个线程的等待状态结束时,该事件会自动变为不发信号的状态。即:如果多个线程在等待向事件发信号,只有一个线程结束其等待状态,它不是等待事件最长的线程,而是优先级最高的线程。
//计算数据的类,使用ManualResetEventSlim类的示例 publicclassCalculator { privateManualResetEventSlimmEvent; publicintResult{get;privateset;} publicCalculator(ManualResetEventSlimev) { this.mEvent=ev; } publicvoidCalculation(intx,inty) { Console.WriteLine("Task{0}startscalculation",Task.CurrentId); Thread.Sleep(newRandom().Next(3000));//随机等待事件 Result=x+y;//计算结果 Console.WriteLine("Task{0}isready",Task.CurrentId); mEvent.Set();//发出完成信号 } } //外部调用的示例: staticvoidMain() { constinttaskCount=10; ManualResetEventSlim[]mEvents=newManualResetEventSlim[taskCount]; WaitHandle[]waitHandles=newWaitHandle[taskCount]; varcalcs=newCalculator[taskCount]; for(inti=0;icalcs[i1].Calculation(i1+1,i1+3)); } for(inti=0;i CountdownEvent类适用于:需要把一个工作任务分配到多个任务中,然后在各个任务结束后合并结果(不需要为每个任务单独创建事件对象)。每个任务不需要同步。CountdownEvent类为所有设置了事件的任务定义了一个初始数字,达到该计数后,就发出信号。
//修改计算类 publicclassCalculator { privateCountdownEventcEvent; publicintResult{get;privateset;} publicCalculator(CountdownEventev) { this.cEvent=ev; } publicvoidCalculation(intx,inty) { Console.WriteLine("Task{0}startscalculation",Task.CurrentId); Thread.Sleep(newRandom().Next(3000));//随机等待事件 Result=x+y;//计算结果 //signaltheevent—completed! Console.WriteLine("Task{0}isready",Task.CurrentId); cEvent.Signal();//发出完成信号 } } //修改方法调用 staticvoidMain() { constinttaskCount=10; CountdownEventcEvent=newCountdownEvent(taskCount); WaitHandle[]waitHandles=newWaitHandle[taskCount]; varcalcs=newCalculator[taskCount]; for(inti=0;icalcs[i1].Calculation(i1+1,i1+3)); } cEvent.Wait();//等待一个事件的信号 Console.WriteLine("allfinished"); for(inti=0;i 5、Barrier类
Barrier类适用于:工作有多个任务分支,并且在所有任务执行完后需要合并的工作情况。与CountdownEvent不同于,该类用于需要同步的参与者。在激活一个任务后,可以动态的添加其他参与者。在主参与者继续之前,可以等待所有其他参与者完成工作。
staticvoidMain() { constintnumberTasks=2; constintpartitionSize=1000000; vardata=newList(FillData(partitionSize*numberTasks)); varbarrier=newBarrier(numberTasks+1);//定义三个参与者:一个主参与者(分配任务者),两个子参与者(被分配任务者) vartasks=newTask [numberTasks];//两个子参与者 for(inti=0;i CalculationInTask(jobNumber,partitionSize,barrier,data));//启动计算任务:可以分开写,以执行多个不同的任务。 } barrier.SignalAndWait();//主参与者以完成,等待子参与者全部完成。 //合并两个结果(LINQ) IEnumerable resultCollection=tasks[0].Result.Zip(tasks[1].Result,(c1,c2)=>{returnc1+c2;}).ToList();//立即求和 charch='a'; intsum=0; foreach(varxinresultCollection) { Console.WriteLine("{0},count:{1}",ch++,x);//输出结果 sum+=x; } Console.WriteLine("mainfinished{0}",sum);//统计过的字符串数量 Console.WriteLine("remaining{0},phase{1}",barrier.ParticipantsRemaining,barrier.CurrentPhaseNumber);//当前参与者信息 } staticint[]CalculationInTask(intjobNumber,intpartitionSize,Barrierbarrier,IList coll) { vardata=newList (coll); intstart=jobNumber*partitionSize;//计算其实下标 intend=start+partitionSize;//计算结束的位置 Console.WriteLine("Task{0}:partitionfrom{1}to{2}",Task.CurrentId,start,end); int[]charCount=newint[26]; for(intj=start;j FillData(intsize) { vardata=newList (size); varr=newRandom(); for(inti=0;i 6、ReaderWriterLockSlim类
该类是使锁定机制允许锁定多个读取器(而不是一个写入器)访问某个资源:如果没有写入器锁定资源,那么允许多个读取器访问资源,但只能有一个写入器锁定该资源。
由它的属性可以读取是否处于堵塞或不堵塞的锁定,如EnterReadLock()和TryEnterReadLock()方法。也可以获得其是否处于写入锁定或非锁定状态,如EnterWriteLock()和TryEnterWriteLock()方法。如果任务需要先读取资源,之后写入资源,可以使用EnterUpgradeableReadLock()或TryEnterUpgradeableReadLock()方法获取可升级的读取锁定。该锁定可以获取写入锁定,而不需要释放读取锁定。
classProgram { privatestaticListitems=newList (){0,1,2,3,4,5}; privatestaticReaderWriterLockSlimrwl=newReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); staticvoidReaderMethod(objectreader) { try { rwl.EnterReadLock(); for(inti=0;i 以上就是c#进程之间的线程同步的详细内容,更多关于c#线程同步的资料请关注毛票票其它相关文章!