简易阻止:Sleep,阻止给定的时间周期;Join,等待另一个线程完成。
一旦被阻止,线程立刻放弃分配给他的CPU资源,并且ThreadState属性值改为WaitSleepJoin。
停止阻止的方式:(1)操作超时(2)通过Thread的Interrupt中断了(3)通过Thread的Abort放弃了.
Thread.Sleep(Timeout.Infinite);//休眠直到中断
Thread.Sleep(TimeSpan.FromSeconds(1));//休眠一秒
Thread.Sleep(0);//暂时的放弃CPU,时间刚刚够其他时间片里的线程执行。
Join方法也接收一个使用毫秒或用TimeSpan类的超时参数,当Join超时时返回false,如果线程已终止,则返回true。(在阻止是Join保持信息汲取,sleep停止信息汲取)。
锁系统:lock,确保只有一个线程访问某资源,不能跨进程,速度快;
Mutex, ,确保只有一个线程访问某资源,可以跨进程,速度中等;
Semaphore,不超过制定数目的线程访问某资源,可以跨进程,速度中等。
任何对所有有关系的线程都可见的对象都可以作为同步对象,但要服从一个硬性规定:它必须是引用类型。也强烈建议同步对象最好私有在类里面(比如一个私有实例字段)防止无意间从外部锁定相同的对象。服从这些规则,同步对象可以兼对象和保护两种作用。
List<string> list = new List<string>();
void Test()
{
lock (list)
{
list.Add("");
//...
}
}
锁并没有以任何方式阻止对同步对象本身的访问,换言之,x.ToString()不会由于另一个线程调用lock(x) 而被阻止,两者都要调用lock(x) 来完成阻止工作。
Interrupt 和 Abort
static void Main(string[] args)
{
Thread t = new Thread(
delegate()
{
try
{
Thread.Sleep(Timeout.Infinite);
}
catch (ThreadInterruptedException)
{
Console.WriteLine("Interrupt!");
}
Console.WriteLine("finally");
}
);
t.Start();
t.Interrupt();
Console.Read();
}
//Interrupt!
//finally
Interrupt中断一个线程仅仅释放其当前的等待状态,并不结束这个线程,后面该干啥干啥!
被阻止的线程也可以通过Abort方法被强制释放,这与调用Interrupt相似,除了用ThreadAbortException异常代替了ThreadInterruptedException异常,此外,异常将被重新抛出在catch里(在试图以有好方式处理异常的时候),直到Thread.ResetAbort在catch中被调用;在这期间线程的ThreadState为AbortRequested。
在Interrupt 与 Abort 之间最大不同在于它们调用一个非阻止线程所发生的事情。Interrupt继续工作直到下一次阻止发生,Abort在线程当前所执行的位置(可能甚至不在你的代码中)抛出异常。
//当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
//可以使用 WaitHandle.WaitOne 方法请求互斥体的所属权。拥有互斥体的线程可以在对 WaitOne 的重复调用中请求相同的互斥体而不会阻止其执行。但线程必须调用 ReleaseMutex 方法同样多的次数以释放互斥体的所属权。Mutex 类强制线程标识,因此互斥体只能由获得它的线程释放。相反,Semaphore 类不强制线程标识。
//互斥体有两种类型:局部互斥体和已命名的系统互斥体。如果使用接受名称的构造函数创建 Mutex 对象,则该对象与具有该名称的操作系统对象关联。已命名的系统互斥体在整个操作系统中都可见,可用于同步进程活动。您可以创建多个 Mutex 对象来表示同一个已命名的系统互斥体,也可以使用 OpenExisting 方法打开现有的已命名系统互斥体。(msdn)
class TreadSafe
{
private static Mutex mut = new Mutex();//可以在构造函数中命名
private const int nums = 3;
private const int arrs = 1;
static void Main(string[] args)
{
for (int i = 0; i < nums; i++)
{
Thread t = new Thread(Go);
t.Name = string.Format("Thread{0}", i);
t.Start();
}
Console.Read();
}
static void Go()
{
for (int i = 0; i < arrs; i++)
{
useResource();
}
}
private static void useResource()
{
mut.WaitOne();
Console.WriteLine(string.Format("{0}进入", Thread.CurrentThread.Name));
Thread.Sleep(100);
Console.WriteLine(string.Format("{0}离开", Thread.CurrentThread.Name));
mut.ReleaseMutex();
}
}
/*使用 Semaphore 类可控制对资源池的访问。线程通过调用 WaitOne 方法(从 WaitHandle 类继承)进入信号量,并通过调用 Release 方法释放信号量。
信号量的计数在每次线程进入信号量时减小,在线程释放信号量时增加。当计数为零时,后面的请求将被阻塞,直到有其他线程释放信号量。当所有的线程都已释放信号量时,计数达到创建信号量时所指定的最大值。
被阻止的线程并不一定按特定的顺序(如 FIFO 或 LIFO)进入信号量。
线程可通过重复调用 WaitOne 方法多次进入信号量。为释放这些入口中的部分或全部,线程可多次调用无参数的 Release 方法重载,也可以调用 Release(Int32) 方法重载来指定要释放的入口数。
Semaphore 类不对 WaitOne 或 Release 调用强制线程标识。程序员负责确保线程释放信号量的次数不能太多。例如,假定信号量的最大计数为 2,并且线程 A 和线程 B 同时进入信号量。如果线程 B 中的编程错误导致它两次调用 Release,则两次调用都成功。这样,信号量的计数已满,当线程 A 最终调用 Release 时便会引发 SemaphoreFullException。
信号量分为两种类型:局部信号量和已命名的系统信号量。如果您使用接受名称的构造函数创建 Semaphore 对象,则该对象与具有该名称的操作系统信号量关联。已命名的系统信号量在整个操作系统中都可见,可用于同步进程活动。您可以创建多个 Semaphore 对象来表示同一个已命名的系统信号量,也可以使用 OpenExisting 方法打开现有的已命名系统信号量。
局部信号量仅存在于您的进程内。您的进程中任何引用局部 Semaphore 对象的线程都可以使用它。每个 Semaphore 对象都是一个单独的局部信号量。*/
class Program
{
private static Semaphore _pool;
private static int _padding;
static void Main(string[] args)
{
_pool = new Semaphore(0, 3);
for (int i = 1; i <= 5; i++)
{
Thread t = new Thread(new ParameterizedThreadStart(Go));
t.Start(i);
}
Thread.Sleep(500);
Console.WriteLine("主线程将信号量增加到3");
_pool.Release(3);
Console.Read();
}
static void Go(object num)
{
Console.WriteLine("Thread{0}开始,并等待Semaphore", num);
_pool.WaitOne();
int padding = Interlocked.Add(ref _padding, 100);//原子操作,两个值相加,把第一个值更新为两值和
Console.WriteLine("Thread{0}进入Semaphore", num);
Thread.Sleep(1000 + padding);
Console.WriteLine("Thread{0} 离出 semaphore.", num);
Console.WriteLine("Thread{0} 前一个信号量数: {1}", num, _pool.Release());
}
}
class Program
{
/*AutoResetEvent 允许线程通过发信号互相通信。 通常,当线程需要独占访问资源时使用该类。
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。 如果 AutoResetEvent 为非终止状态,则线程会被阻止,并等待当前控制资源的线程通过调用 Set 来通知资源可用。
调用 Set 向 AutoResetEvent 发信号以释放等待线程。 AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。 如果没有任何线程在等待,则状态将无限期地保持为终止状态。
如果当 AutoResetEvent 为终止状态时线程调用 WaitOne,则线程不会被阻止。 AutoResetEvent 将立即释放线程并返回到非终止状态。*/
static AutoResetEvent wh = new AutoResetEvent(false);
static void Main()
{
new Thread(Go).Start();
Thread.Sleep(2000);
wh.Set();//释放
Console.Read();
}
static void Go()
{
Console.WriteLine("waitting..........");
wh.WaitOne();//暂停
Console.WriteLine("OK");
}
}
//生产者/消费者队列
class ProducerCustomerQueue : IDisposable
{
AutoResetEvent wh = new AutoResetEvent(false);
Thread worker;
object locker = new object();
Queue<string> tasks = new Queue<string>();
public ProducerCustomerQueue()
{
worker = new Thread(work);
worker.Start();
}
public void EnqueueTask(string task)
{
lock (locker)
{
tasks.Enqueue(task);
}
wh.Set();
}
void work()
{
while (true)
{
string task = null;
lock (locker)
{
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) //tag
{
return;
}
}
if (task != null)
{
Console.WriteLine("执行任务" + task);
Thread.Sleep(1000);
}
else
{
wh.WaitOne();//没有任务了,等待信号
}
}
}
}
#region IDisposable 成员
public void Dispose()
{
EnqueueTask(null);//goto:tag.告诉消费者退出
worker.Join();//等待消费者线程完成
wh.Close();//释放所有资源
}
#endregion
}
class TreadSafe
{
static void Main(string[] args)
{
using (ProducerCustomerQueue q = new ProducerCustomerQueue())
{
q.EnqueueTask("hello");
for (int i = 0; i < 10; i++)
{
q.EnqueueTask("say " + i);
}
q.EnqueueTask("Bye");
}
Console.Read();
}
}
来源:https://www.cnblogs.com/hometown/p/3243017.html