多线程
启动线程方式
- new Thread(), .Start(); 启动线程
- Task.Factory.StartNew(); 启动线程
- 线程池启动TreadPool.QueueUserWorkItem 启动工作线程
- BackgroundWorker .RunWorkerAsync 启动 DoWork 方法
- Parallel.Invoke 并行执行 并且会阻塞 主线程,只有当Invoke中的所有任务完毕 才会进行下一步
BackgroundWorker
DoWork事件 触发子线程空间 通过 RunWorkerAsync 方法 触发
ProgressChanged事件 主线程空间 通过 ReportProgress 方法触发
RunWorkerCompleted 事件 主线程空间 DoWork方法执行完成后,自动触发
TreadPool
QueueUserWorkItem 启动一个工作线程
SetMaxThreads 设置线程池的最大线程数
SetMinThreads 设置线程池的最小线程数
Parallel
Invoke - 并行执行 invoke中所有的委托
线程切换
从子线程上下文空间切换到主线程空间,以便更新界面UI
- 如果有控件, 可以用控件的 Invoke 方法
- 非控件类,可以用 BackgroundWorker 类
- SynchronizationContext 的 Send 方法
线程同步信号
在多线程并行时,使用同步信号可以阻塞线程运行,以便按照流程执行代码
- 自动同步信号 AutoResetEvent
- 手动同步信号 ManualResetEvent
AutoResetEvent 自动重置同步信号事件
此类可以控制在多线程并行情况下,阻塞或依次执行
-
AutoResetEvent(bool initialState) 构造方法,创建一个自动同步信号事件
- initialState :true 信号是释放的,代表 第一次调用 WaitOne 时不会阻塞线程
- initialState :false 信号是锁定的,代表 第一次调用 WaitOne 时会阻塞线程
-
**WaitOne **() 阻塞当前线程,直到 使用 Set 方法释放线程
-
Set() 发送一次释放信号,将释放由 WaitOne 方法阻塞的其中一个线程后, 将事件状态重置为锁定
// 创建一个同步信号实例
private static AutoResetEvent event_1 = new AutoResetEvent(false);
/// <summary>
/// 开始测试
/// </summary>
public static void TestAutoResetEvent2()
{
for (int i = 0; i < 3; i++)
{
// 启动三个线程
var thread = new Thread(UpdateWithLock);
thread.Start();
}
}
internal static void ReleaseOnce()
{
// 发送一次释放信号
event_1.Set();
}
private static void UpdateWithLock()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
event_1.WaitOne();
Console.WriteLine("{0} is released from AutoResetEvent #1.", name);
}
ManualResetEvent 手动同步信号事件
此类可以控制在多线程并行情况下,阻塞或放行全部线程
-
ManualResetEvent (bool initialState) 构造方法,创建一个手动同步信号事件
- initialState :true 信号是释放的,代表 调用 WaitOne 时不会阻塞线程
- initialState :false 信号是锁定的,代表 调用 WaitOne 时会阻塞线程
-
**WaitOne **() 如果当前 信号是阻塞的 , 那么就会阻塞当前线程
-
Set() 修改事件状态为释放的,将释放由 WaitOne 方法阻塞的所有线程
-
Reset() 修改事件状态为锁定的, 之后调用的 WaitOne 方法将阻塞线程
// 创建一个同步信号实例
private static ManualResetEvent event_1 = new ManualResetEvent(true);
/// <summary>
/// 开始测试
/// </summary>
public static void TestManualResetEvent2()
{
for (int i = 0; i < 3; i++)
{
// 启动三个线程
var thread = new Thread(UpdateWithLock);
thread.Name = "thread_" + i;
thread.Start();
}
}
internal static void ReleaseEvent()
{
// 将事件状态置为释放
event_1.Set();
}
internal static void LockEvent()
{
// 将事件状态置为锁定
event_1.Reset();
}
private static void UpdateWithLock()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine("{0} waits on ManualResetEvent #1.", name);
event_1.WaitOne();
Console.WriteLine("{0} is released from ManualResetEvent #1.", name);
}
线程锁
- 自旋锁 SpinLock
- 互斥锁 Mutex
- 混合锁 lock & Monitor
- Interlocked
SpinLock 自旋锁
如果需要锁定执行的关键代码量比较少,则SpinLock效率会比lock关键字高
用法:
// 定义
SpinLock _spinlock = new SpinLock();
private static void UpdateWithSpinLock(Data d, int i)
{
bool lockTaken = false;
try
{
// 锁定资源
_spinlock.Enter(ref lockTaken);
// 需要锁定执行的代码
}
finally
{
// 释放锁
if (lockTaken) _spinlock.Exit(false);
}
}
Mutex 互斥锁
当多个线程需要同时访问某一个共享资源时,Mutex 可以确保一次只有一个线程能使用到该资源
- bool WaitOne(int timeout) 锁定线程并等待 timeOut 毫秒后,超时
- void ReleaseMutex() 释放被锁定的资源
用法:
// 创建一个互斥锁对象
private static Mutex mut = new Mutex();
private static void UseResource()
{
// Wait until it is safe to enter.
Console.WriteLine("{0} is requesting the mutex",
Thread.CurrentThread.Name);
mut.WaitOne();
Console.WriteLine("{0} has entered the protected area",
Thread.CurrentThread.Name);
// Place code to access non-reentrant resources here.
// Simulate some work.
Thread.Sleep(500);
Console.WriteLine("{0} is leaving the protected area",
Thread.CurrentThread.Name);
// Release the Mutex.
mut.ReleaseMutex();
Console.WriteLine("{0} has released the mutex", Thread.CurrentThread.Name);
}
Monitor 混合锁
说明:
- 当多个线程需要同时访问某一个共享资源时,Monitor 可以确保一次只有一个线程能使用到该资源
- Monitor 是全局的,可以从任何上下文来调用
- Monitor 需要唯一的锁定对象来关联
方法:
- void Enter(object obj) 锁定线程并等待 timeOut 毫秒后,超时释放
- void Exit(object obj) 释放被锁定的资源
用法:
// 创建一个互斥锁对象
private static object _lock = new Object();
private static void UseResource()
{
try
{
Monitor.Enter(_lock);
_queue.Enqueue(d);
}
finally
{
Monitor.Exit(_lock);
}
}