C# 线程同步的方法
一、进程内部的线程同步
1、使用lock,用法如下:
privatestaticreadonlyobjectSeqLock=newobject();
privatevoidPrint()
{
lock(SeqLock)
{
Console.WriteLine("test");
}
}
特性:只能传递对象,无法设置等待超时
2、使用:InterLocked(原子操作)
其在System.Threading命名空间下,Interlocked实际是类控制计数器,从而实现进程的同步,其很容易实现生产者消费者模型
//缓冲区,只能容纳一个字符
privatestaticcharbuffer;
//标识量(缓冲区中已使用的空间,初始值为0)
privatestaticlongnumberOfUsedSpace=0;
staticvoidMain(string[]args)
{
//线程:写入者
ThreadWriter=newThread(delegate()
{
stringstr="这里面的字会一个一个读取出来,一个都不会少,,,";
for(inti=0;i<24;i++)
{
//写入数据前检查缓冲区是否已满
//如果已满,就进行等待,直到缓冲区中的数据被进程Reader读取为止
while(Interlocked.Read(refnumberOfUsedSpace)==1)
{
Thread.Sleep(50);
}
buffer=str[i];//向缓冲区写入数据
//写入数据后把缓冲区标记为满(由0变为1)
Interlocked.Increment(refnumberOfUsedSpace);
}
});
//线程:读出者
ThreadReader=newThread(delegate()
{
for(inti=0;i<24;i++)
{
//读取数据前检查缓冲区是否为空
//如果为空,就进行等待,直到进程Writer向缓冲区中写入数据为止
while(Interlocked.Read(refnumberOfUsedSpace)==0)
{
Thread.Sleep(50);
}
charch=buffer;//从缓冲区读取数据
Console.Write(ch);
Interlocked.Decrement(refnumberOfUsedSpace);
}
});
//启动线程
Writer.Start();
Reader.Start();
Console.ReadKey();
3、使用Monitor
其中Monitor.Enter()和lock相同
Monitor.Enter(obj){
//Synchronizedpart
}finally{
Monitor.Exit(obj);
}
TryEnter则可设置等待时间等
boollockTaken=false;
Monitor.TryEnter(obj,500,reflockTaken);
if(lockTaken){
try
{
//Synchronizedpart
}
finally
{
Monitor.Exit(obj);
}
}else{
//don'taquirethelock,excuteotherparts
}
二、进程间的同步
1.WaitHandle:
封装等待对共享资源进行独占访问的操作系统特定的对象。WaitHandle:是一个抽象类,我们一般不直接用,而是用它的派生类:
AutoResetEvent、EventWaitHandle、ManualResetEvent、Mutex、Semaphore
这个抽象类的方法如下:
WaitOne():等待一个信号的出现,可设置超时;
WaitAll():等待多个信号的出现,可设置超时;
WaitAny():等待任意一个信号的出现,可设置超时;
2、Mutex:与Monitor类似,只有一个线程能够获取锁定。利用WaitOne()获取锁定,利用ReleaseMutex()解除锁定。构造函数使用如下:
boolisNew=false; mutex=newMutex(false,"Mutex1",outisNew);
参数1:锁创建后是否由主调线程拥有。如果设为true,相当于调用了WaitOne(),需要释放,否则其他线程无法获取锁;
参数2:锁名称,可通过OpenExist()或TryOpenExist()打开已有锁,因为操作系统识别有名称的互锁,所以可由不同的进程共享。若锁名称为空,就是未命名的互锁,不能在多个进程之间共享;
参数3: 是否为新创建的互锁;
下面的例子演示Mutex在进程之间的使用: classProgram
privatestaticMutexmutex=null;
staticvoidMain(string[]args)
{
boolisNew=false;
mutex=newMutex(false,"Mutex1",outisNew);
Console.WriteLine("MainStart....");
mutex.WaitOne();
Console.WriteLine("AquireLockandRunning....");
Thread.Sleep(10000);
mutex.ReleaseMutex();
Console.WriteLine("ReleaseLock....");
Console.WriteLine("Mainend....");
Console.ReadLine();
}
}
连续2次运行这个控制台程序的exe,结果如下,首先运行的获取Mutex1互锁,后面运行的会等待直到前面运行的释放Mutex1互锁。
3.Semaphore:信号量的作用于互斥锁类似,但它可以定义一定数量的线程同时使用。下面是构造函数:
boolisNew=false; semaphore=newSemaphore(3,3,"semaphore1",outisNew);
参数1:创建后,最初释放的锁的数量,如参数1设为2,参数2设为3,则创建后只有2个锁可用,另1个已经锁定;
参数2:定义可用锁的数量;
参数3: 信号量的名称,与Mutex类似;
参数4:是否为新创建的互锁;
以下例子创建了信号量“semaphore1”,利用Parallel.For()同步运行Func1(),在Func1()中,当线程获取信号量锁,释放锁或等待超时,都会在控制台里输出,
classProgram
{
privatestaticSemaphoresemaphore=null;
staticvoidMain(string[]args)
{
Console.WriteLine("MainStart....");
boolisNew=false;
semaphore=newSemaphore(3,3,"semaphore1",outisNew);
Parallel.For(0,6,Func1);
Console.WriteLine("Mainend....");
Console.ReadLine();
}
staticvoidFunc1(intindex)
{
Console.WriteLine("Task{0}Start....",Task.CurrentId);
boolisComplete=false;
while(!isComplete)
{
if(semaphore.WaitOne(1000))
{
try
{
Console.WriteLine("Task{0}aquirelock....",Task.CurrentId);
Thread.Sleep(5000);
}
finally
{
semaphore.Release();
Console.WriteLine("Task{0}releaselock....",Task.CurrentId);
isComplete=true;
}
}
else
{
Console.WriteLine("Task{0}timeout....",Task.CurrentId);
}
}
}
运行结果如下,线程1,2,3首先获取信号量锁,线程4,5,6在等待,直到1,2,3释放,
4.AutoResetEvent类:
可以使用事件通知其他任务,构造函数为publicAutoResetEvent(boolinitialState)。
当initialState=true,处于signaled模式(终止状态),调用waitone()也不会阻塞任务,等待信号,调用Reset()方法,可以设置为non-signaled模式;
当initialState=fasle,处于non-signaled模式(非终止状态),调用waitone()会等待信号阻塞当前线程(可以在多个线程中调用,同时阻塞多个线程),直到调用set()发送信号释放线程(调用一次,只能释放一个线程),一般使用这种方式;
以下例子创建5个任务,分别调用waitone()阻塞线程,接着每隔2s调用set(),
privatestaticAutoResetEventautoReset=newAutoResetEvent(false);
staticvoidMain(string[]args)
{
Console.WriteLine("MainStart....");
for(inti=0;i<5;i++)
{
Task.Factory.StartNew(()=>
{
Console.WriteLine("{0}Start....",Task.CurrentId);
autoReset.WaitOne();
Console.WriteLine("{0}Continue....",Task.CurrentId);
});
}
for(inti=0;i<5;i++)
{
Thread.Sleep(2000);
autoReset.Set();
}
Console.WriteLine("Mainend....");
Console.ReadLine();
}
运行结果每次顺序略有不同,释放是随机的:
5.ManualResetEvent类:功能基本上和AutoSetEvent类似,但又一个不同点:
使用AutoSetEvent,每次调用set(),切换到终止模式,只能释放一个waitone(),便会自动切换到非终止模式;但ManualResetEvent,调用set(),切换到终止模式,可以释放当前所有的waitone(),需要手动调用reset()才能切换到非终止模式。
以下例子说明了这个不同的:
privatestaticManualResetEventmanualReset=newManualResetEvent(false);
staticvoidMain(string[]args)
{
Console.WriteLine("MainStart....");
for(inti=0;i<5;i++)
{
Task.Factory.StartNew(()=>
{
Console.WriteLine("{0}Start....",Task.CurrentId);
manualReset.WaitOne();
Console.WriteLine("{0}Continue....",Task.CurrentId);
});
}
Thread.Sleep(2000);
manualReset.Set();
manualReset.WaitOne();
Console.WriteLine("itdoesn'tworknow,Maincontinue....");
manualReset.Reset();
manualReset.WaitOne();
Console.WriteLine("Mainend....");
Console.ReadLine();
}
以上就是C#线程同步的方法的详细内容,更多关于c#线程同步的资料请关注毛票票其它相关文章!