Interrupt和Abort
class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(Run);
thread.Start();
Thread.Sleep(2500);
thread.Interrupt();//中断处于 WaitSleepJoin 线程状态的线程。注意是中断,相当于continue
Thread.Sleep(3000);
thread.Abort(); //thread.Abort();//终止线程
}
static void Run()
{
for (int i = 1; i < 10; i++)
{
try
{
Thread.Sleep(1000);
Console.WriteLine(i.ToString());
}
catch (ThreadInterruptedException)
{
Console.WriteLine("中断线程");
}
catch(ThreadAbortException)
{
Console.WriteLine("终止线程");
Console.ReadKey();
}
}
Console.WriteLine("这句代码不会执行");//终止线程 走完case 里的代码就终止了,这句代码不执行
}
}
可以看出thread.Interrupt()中断了第三个循环,thread.Abort()是终止了整个线程.
锁
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Thread thread1 = new Thread(Run1);
thread1.Start();
}
Console.ReadKey();
}
static object lockObjec=new object();
static void Run1()
{
Monitor.Enter(lockObjec);
Thread.Sleep(1000);
Console.WriteLine(DateTime.Now);
Monitor.Exit(lockObjec);
//lock (lockObjec) lock语法糖等同于 Monitor.Enter()和Monitor.Exit()
//{
// Thread.Sleep(1000);
// Console.WriteLine(DateTime.Now);
//}
}
}
不加锁执行结果
加锁执行结果
可见,锁实现了锁里代码同时只能有一个线程就去执行
Monitor.Pulse,Monitor.Wait 线程同步
class Program
{
static void Main(string[] args)
{
Thread thread1 = new Thread(Run1);
Thread thread2 = new Thread(Run2);
thread2.Start();
Thread.Sleep(1000);
thread1.Start();
Console.ReadKey();
}
static object lockObjec=new object();
static void Run1(object id)
{
for (int i = 1; i < 20;)
{
lock (lockObjec)
{
Monitor.Pulse(lockObjec);//通知等待队列中的线程锁定对象状态的更改(做好准备,一定锁被释放就可以进入)。
Console.WriteLine(i);
i = i + 2;
Monitor.Wait(lockObjec); //释放对象上的锁并阻止当前线程,直到它重新获取该锁(进去等待队列,并等 Monitor.Pulse()的通知)。
}
}
}
static void Run2(object id)
{
for (int i = 2; i < 20;)
{
lock (lockObjec)
{
Monitor.Wait(lockObjec);
Console.WriteLine(i);
i = i + 2;
Monitor.Pulse(lockObjec);
}
}
}
}
可以看到,哪怕thread2先start一秒,输出顺序也是1到19的有序输出。做到了线程间同步
ReadWriterLock类
ReadWriterLock类可以实现 单个线程写入,多个线程读取,比如读写数据库就有这种需求
类里方法:
AcquireWriterLock: 获取写入锁。
ReleaseWriterLock:释放写入锁。
AcquireReaderLock: 获取读锁。
ReleaseReaderLock:释放读锁。
UpgradeToWriterLock:将读锁转为写锁。
DowngradeFromWriterLock:将写锁还原为读锁。
Mutex
提供了WatiOne和ReleaseMutex来确保只有一个线程来访问共享资源,与Lock功能相似
static Mutex metux = new Mutex();
static void Run1()
{
metux.WaitOne();
Thread.Sleep(1000);
Console.WriteLine(DateTime.Now);
metux.ReleaseMutex();
}
Mutex实现单例模式
static void Main(string[] args)
{
bool flag = false;
Mutex mutex = new Mutex(true, "mutex1", out flag);
//第一个参数:true--给调用线程赋予互斥体的初始所属权
//第一个参数:互斥体的名称,没命名的Mutex是局部的,命名的Mutex存在于整个操作系统
//第三个参数:返回值,如果调用线程已被授予互斥体的初始所属权,则返回true
if (!flag)
{
Console.Write("A instance has already run");
Environment.Exit(1);//退出程序
}
Console.ReadLine();
}
Interlocked
为多个线程共享的变量提供原子操作。
原子访问,指的是一个线程在访问某个资源的同时能够保证没有其他线程会在同一时刻访问同一资源。
所以虽然无锁,也是线程安全的,只是对共享变量进行一些简单的操作可用。比如下列方法
static int count = 0;
static void Run1( )
{
Interlocked.Increment(ref count);//以原子操作的形式递增指定变量的值并存储结果。
Interlocked.Add(ref count, 1);//对两个 32 位整数进行求和并用和替换第一个整数,上述操作作为一个原子操作完成。
Interlocked.Exchange(ref count, 3);// 以原子操作的形式,将 32 位有符号整数设置为指定的值并返回原始值。
Interlocked.CompareExchange(ref count, 3, 5);//比较两个 32 位有符号整数是否相等,如果相等,则替换其中一个值。
}
ManualResetEvent和AutoResetEvent
class Program
{
static void Main(string[] args)
{
Thread thread1 = new Thread(Run1);
Thread thread2 = new Thread(Run2);
thread1.Start();
thread2.Start();
Thread.Sleep(1000);
Console.WriteLine("给出信号");
mr.Set();//将事件状态设置为终止(非阻塞)状态,允许一个或多个等待线程继续。
Console.ReadKey();
}
//如果为 true,则将初始状态设置为终止(非阻塞);如果为 false,则将初始状态设置为非终止(阻塞)。
static ManualResetEvent mr = new ManualResetEvent(false);
//static AutoResetEvent ar = new AutoResetEvent(false);
static void Run1()
{
Console.WriteLine("线程1等待");
mr.WaitOne();//状态为非终止(阻塞)才会等待。
Console.WriteLine("线程1继续");
}
static void Run2()
{
Console.WriteLine("线程2等待");
mr.WaitOne();//状态为非终止(阻塞)才会等待。
Console.WriteLine("线程2继续");
}
}
将ManualResetEvent改为AutoResetEvent,执行结果为
ManualResetEvent和AutoResetEvent区别
在Set()后,当某个线程得到信号后,AutoResetEvent会自动又将信号置为非终止(阻塞)状态,其他调用WaitOne的线程只有继续等待;而ManualResetEvent不会自动将信号置为为非终止(阻塞),所有线程都可以执行。除非调用Reset()。
Semaphore
控制能并发的线程数
class Program
{
static void Main(string[] args)
{
Thread thread1 = new Thread(Run1);
Thread thread2 = new Thread(Run2);
Thread thread3 = new Thread(Run3);
thread1.Start();
thread2.Start();
thread3.Start();
//sem.Release(3);可以实时更改信号量个数
Console.ReadKey();
}
// 默认设置的信号量个数 和 最大信号量个数
//里面是采用计数器来来分配信号量,当你WaitOne的时候,信号量自减,当Release的时候,信号量自增,
//然而当信号量为0的时候,后续的线程就不能拿到WaitOne了,所以必须等待先前的线程通过Release来释放
static Semaphore sem = new Semaphore(1, 10);
static void Run1()
{
Console.WriteLine(DateTime.Now+ "--线程1开始");
sem.WaitOne();
Thread.Sleep(2000);
Console.WriteLine(DateTime.Now + "--线程1结束");
sem.Release();
}
static void Run2()
{
Console.WriteLine(DateTime.Now + "--线程2开始");
sem.WaitOne();
Thread.Sleep(2000);
Console.WriteLine(DateTime.Now + "--线程2结束");
sem.Release();
}
static void Run3()
{
Console.WriteLine(DateTime.Now + "--线程3开始");
sem.WaitOne();
Thread.Sleep(2000);
Console.WriteLine(DateTime.Now + "--线程3结束");
sem.Release();
}
}
可以看到每个线程结束时间都在上一个线程结束时间上增加了2秒,因为设置的允许并发线程数为1。等待的线程要等到Release()才能继续执行。
如果允许并发线程数为2:static Semaphore sem = new Semaphore(2, 10);
结果为:
可以看到线程1和线程2基本上是同一时间结束的,而线程3又过了2秒才结束,因为允许的并发线程数为2.
与Mutex一样,Semaphore如果取了名字,如static Semaphore sem = new Semaphore(2, 10,“sem1”);
将程式运行2个实例,有执行的结果(两个13秒,两个15秒,两个17秒)可以看出
取了名字的Semaphore是作用于整个操作系统(所有程式共用此Semaphore允许的并发线程数)。
ThreadPool
static void Main(string[] args)
{
int workerThreads;
int completionPortThreads;
// workerThreads:
// 线程池中辅助线程的最大数目。
// completionPortThreads:
// 线程池中异步 I/O 线程的最大数目。
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("线程池中辅助线程的最大数目:" + workerThreads + "; 线程池中异步 I/O 线程的最大数目:" + completionPortThreads);
Console.ReadKey();
ThreadPool.SetMaxThreads(8, 10);
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("线程池中辅助线程的最大数目:" + workerThreads + "; 线程池中异步 I/O 线程的最大数目:" + completionPortThreads);
Console.ReadKey();
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
ThreadPool.SetMaxThreads(5, 3);
ThreadPool.QueueUserWorkItem(Run1);
Console.ReadKey();
AutoResetEvent ar = new AutoResetEvent(false);
Console.WriteLine(DateTime.Now);
ThreadPool.RegisterWaitForSingleObject(ar, Run2, null, Timeout.Infinite, false);//等待继续指令
Thread.Sleep(3000);
ar.Set();//允许等待线程继续
Thread.Sleep(5000);
ar.Set();//允许等待线程继续
Console.ReadKey();
Console.WriteLine(DateTime.Now);
RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(ar, Run2, null, 2000, false);//每隔2秒重置计时器并等待,直到注销等待
Thread.Sleep(10000);
handle.Unregister(ar);//注销等待操作
Console.ReadKey();
}
private static void Run1(object state)
{
Console.WriteLine("线程:" + Thread.CurrentThread.ManagedThreadId + "开始工作 "+DateTime.Now);
}
private static void Run2(object state, bool timedOut)
{
Console.WriteLine("线程:" + Thread.CurrentThread.ManagedThreadId + "开始工作 " + DateTime.Now);
}