标签:静态 无法获得 and 用户 sys 重置 数据库 ide 打印
将学习多线程中使用共享资源的常用技术。
Mutex
semaphoreSlim
autoResetEvent
manualResetSlim
countDownEvent
Barrier
ReaderWriterLockSlim
SpinWait
Mutex 互斥
semaphoreSlim 信号灯限时。
autoResetEvent 自动重置事件
manualResetSlim 重置Slim手动
countDownEvent 倒计时事件
Barrier 障碍物
ReaderWriterLockSlim
SpinWait 旋转等待。
2.1 简介
重新设计程序来移除共享状态,从而去掉复杂的同步构造
。如果必须使用共享状态,就是使用原子操作
。就是只有当前操作完成后,其他线程才能执行其他操作。因此,你无须实现其他线程等待当前操作完成,这就避免了使用锁,也排除了死锁的情况。2.2 执行基本的原子操作
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication3._2
{
public class Class2_2
{
internal abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
internal static void TestCounter(CounterBase c)
{
for (int i = 0; i < 10000; i++)
{
c.Increment();
c.Decrement();
}
}
internal class Counter : CounterBase
{
private int _count;
public int Count { get { return _count; } }
public override void Decrement()
{
_count--;
}
public override void Increment()
{
_count++;
}
}
internal class CounterNoLock : CounterBase
{
private int _count;
public int Count { get { return _count; } }
public override void Decrement()
{
Interlocked.Decrement(ref _count);
}
public override void Increment()
{
Interlocked.Increment(ref _count);
}
}
}
}
static void Main(string[] args)
{
#region 2.2
Console.WriteLine("Incorret counter");
var c = new Class2_2.Counter();
var t1 = new Thread(() => Class2_2.TestCounter(c));
var t2 = new Thread(() => Class2_2.TestCounter(c));
var t3 = new Thread(()=>Class2_2.TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("total count:{0}",c.Count);
Console.WriteLine("----------");
Console.WriteLine("correct counter");
var c1 = new Class2_2.CounterNoLock();
t1 = new Thread(()=>Class2_2.TestCounter(c1));
t2 = new Thread(()=>Class2_2.TestCounter(c1));
t3 = new Thread(()=>Class2_2.TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("total count:{0}", c1.Count);
Console.ReadKey( );
#endregion
}
c.Count
结果是随机的,而c1.Count
借助Interlocked
类,不用锁定任何对象也能获得正确结果。Interlocked提供了Increment、Decrement和add等基本数学操作的原子方法。2.3 使用Mutex类
//Main方法
#region 2.3
const string MutexName = "abcd";
using (var m = new Mutex(false, MutexName))
{
if(!m.WaitOne(5000,false))
{
Console.WriteLine("second instance is running");
Console.ReadLine();
m.ReleaseMutex();
}
else
{
Console.WriteLine("running");
Console.ReadLine();
m.ReleaseMutex();
}
}
#endregion
2.4使用SemaphoreSlim类(信号量)
static SemaphoreSlim _se = new SemaphoreSlim(4);
static void AccessDataBase(string name,int s)
{
Console.WriteLine("{0} 等待访问数据库 ",name);
_se.Wait();
Console.WriteLine("{0} 被允许访问数据库",name);
Thread.Sleep(s*1000);
Console.WriteLine("{0} 结束掉了 ",name);
_se.Release();
}
static void Main(string[] args)
{
for (int i = 0; i <=6; i++)
{
string threadName = "thread" + i;
int s = 2 + 2 * i;
var t = new Thread(() => AccessDataBase(threadName, s));
t.Start();
}
}
工作原理
- 主程序启动时,创建SemaphoreSlim的一个实例,在构造函数中指定允许的并发线程数量。main方法启动了6个不同线程,每个都尝试获取数据库的访问。但是信号量系统限制了并发量是4个线程。当有4个获取了数据库访问后,其他两个需要等待,直到之前线程完成工作调用release方法来发出信号。
- 这里我们使用了混合模式,其允许我们在等待时间很短情况下无需使用上下文切换。然后
semaphore
类使用内核方式。一般没必要使用它,而在跨程序同步的场景下可以使用它。
2.5 使用AutoRestEvent类
#region 2.5
private static AutoResetEvent _w = new AutoResetEvent(false);
private static AutoResetEvent _m = new AutoResetEvent(false);
static void Process(int s)
{
Console.WriteLine("开始一个工作...");
Thread.Sleep(s*1000);
Console.WriteLine("正在工作");
_w.Set();
Console.WriteLine("等待主线程完成它的工作");
_m.WaitOne();
Console.WriteLine("开始第二个操作...");
Thread.Sleep(s*1000);
Console.WriteLine("工作完成");
_w.Set();
}
#endregion
static void Main(string[] args)
{
#region 2.5
var t = new Thread(()=>Process(10));
t.Start();
Console.WriteLine("等待其他线程完成");
_w.WaitOne();
Console.WriteLine("第一个操作完成");
Console.WriteLine("在主线程上执行操作");
_w.WaitOne();
Console.WriteLine("第二个线程完成!");
#endregion
}
2.6 使用ManualRestEventSlim类
#region 2.6
static ManualResetEventSlim _m = new ManualResetEventSlim(false);
static void TravelThroughGates(string threadName,int seconds)
{
Console.WriteLine("{0} 睡着了", threadName);
Thread.Sleep(seconds * 1000);
Console.WriteLine("{0} 等待开门!",threadName);
_m.Wait();
Console.WriteLine("{0} 进入大门!",threadName);
}
#endregion
static void Main(string[] args)
{
#region 1.
#endregion
#region 2.6
var t1 = new Thread(()=> TravelThroughGates("thread1",5));
var t2 = new Thread(()=> TravelThroughGates("thread2",6));
var t3 = new Thread(() => TravelThroughGates("thread3",12));
t1.Start();
t2.Start();
t3.Start();
Thread.Sleep(3000);
Console.WriteLine("门打开了!");
_m.Set();
Thread.Sleep(2000);
_m.Reset();
Console.WriteLine("门关闭了1!");
Thread.Sleep(10000);
Console.WriteLine("门第二次打开了!");
_m.Set();
Thread.Sleep(2000);
Console.WriteLine("门关闭了2!");
_m.Reset();
Console.ReadLine( );
#endregion
}
_m.Set
时,相当于打开大门从而允许准备好的线程接收信号并继续工作。然后线程3还在睡眠状态,没有赶上时间。当调用_m.Reset()
相当于关闭大门。最后一个线程已经准备好执行,但是不得不等待下一个信号,即要等待好几秒。2.7 使用CountDownEvent类
#region 2.7
static CountdownEvent _c = new CountdownEvent(2);
static void PerformOperation(string message,int s)
{
Thread.Sleep(s*1000);
Console.WriteLine(message);
_c.Signal();//向CountdownEvent 注册信号,同时减少其计数。
}
#endregion
static void Main(string[] args)
{
#region 1.
#endregion
#region 2.7
Console.WriteLine("开始两个操作");
var t1 = new Thread(()=>PerformOperation("操作1完成了",4));
var t2 = new Thread(()=>PerformOperation("操作2完成了",8));
t1.Start();
t2.Start();
_c.Wait();
Console.WriteLine("两个操作完成了!");
_c.Dispose();
Console.ReadKey();
#endregion
}
工作原理
- 当主程序启动时,创建了一个CountdownEvent实例,在其构造函数中指定了当两个操作完成时会发出信号。然后启动两个线程,当执行完成后会发出信号。一旦第二个线程完成,主线程会从等待Countdownevent的状态中返回并继续执行。针对需要等待多个异步操作完成的情形,使用本方式非常便利。然而有一个重大缺点。如果调用
_c.Signal()
没有到指定的次数,那么_c.Wait()
将一直等待。请确保使用countdownEvent时,所有线程完成后都要调用signal方法。
2.8使用Barrier类
*微软类库解释该作用:使多个任务能够采用并行方式依据某种算法在多个阶段中协同工作。
c #region 2.8 static Barrier _b = new Barrier(2, b => Console.WriteLine("end of phase {0}",b.CurrentPhaseNumber+1));//获取屏障的当前阶段的编号。 static void PlayMusic(string name,string message,int s) { for (int i = 0; i < 3; i++) { Console.WriteLine("---------"); Thread.Sleep(s*1000); Console.WriteLine("{0} starts to {1}",name,message); Thread.Sleep(s*1000); Console.WriteLine("{0} finishes to {1}",name,message); _b.SignalAndWait(); } } #endregion static void Main(string[] args) { var t1 = new Thread(()=>PlayMusic("吉他手","play an amzing solo",5)); var t2 = new Thread(()=> PlayMusic("歌手 ","sing his song",2)); t1.Start(); t2.Start(); Console.Read(); } #endregion
工作原理
- 创建了Barrier类,指定了我们想要同步两个线程。在两个线程中的任何一个调用了
_b.SignalAndWait
方法后,会执行一个回调函数来打印出阶段。
每个线程将向barrier发送两次信号,所以会有两个阶段。每次这两个线程调用signalAndWait方法时,barrier将执行回调函数。这在多线程迭代运算中非常有用,可以在每个迭代结束前执行一些计算。当最后一个线程调用signalandwait方法时可以在迭代结束时进行交互。
2.10 使用SpinWait类
#region 2.10
static volatile bool _isOver = false;
static void UserModeWait()
{
while (!_isOver)
{
Console.WriteLine(".");
}
Console.WriteLine();
Console.WriteLine("waiting is complete");
}
static void HybridSpinWait()
{
var w = new SpinWait();
while (!_isOver)
{
w.SpinOnce();
Console.WriteLine(w.NextSpinWillYield);
}
Console.WriteLine("waiting is complete");
}
#endregion
static void Main(string[] args)
{
#region 2.10
var t1 = new Thread(UserModeWait);
var t2 = new Thread(HybridSpinWait);
Console.WriteLine("运行用户模式等待");
t1.Start();
Thread.Sleep(20);
_isOver = true;
Thread.Sleep(1000);
_isOver = false;
Console.WriteLine("运行混合自旋等待结构等待");
t2.Start();
Thread.Sleep(5);
_isOver = true;
Console.Read();
#endregion
}
标签:静态 无法获得 and 用户 sys 重置 数据库 ide 打印
原文地址:https://www.cnblogs.com/anjun-xy/p/11832743.html