1、lock
锁定的是一个引用类型,值类型不能被锁定,但应该避免锁定一个string,因为string的存储方式是不样的。
主要注意的就是被锁的这个对象。
class Program
{
private static readonly object objLock = new object();
static int te = 10;
static int num = 0;
static void Main(string[] args)
{
for (int i = 0; i < 10000; i++)
{
Task.Run(Method);
}
Thread.Sleep(100);
Console.WriteLine(num.ToString());
Console.ReadLine();
}
public static void Method()
{
lock (objLock)
{
num++;
}
// num++;
}
}
2、InterLocked
如果一个变量被多个线程修改,读取。可以用InterLocked
。
计算机上不能保证对一个数据的增删是原子性的,因为对数据的操作也是分步骤的:
1.将实例变量中的值加载到寄存器中。
2.增加或减少该值。
3.在实例变量中存储该值。
InterLocked为多线程共享的变量提供原子操作。
InterLocked提供了需要原子操作的方法:
class Program
{
public static int value1 = 0;
public static int value2 = 0;
static void Main(string[] args)
{
TestIncrement();
Thread.Sleep(2000);
Console.WriteLine(value1+"\n"+value2);
Console.ReadLine();
}
public static void TestIncrement()
{
for(int i = 0; i < 5; i++){
Task.Run(IncrementValue);
}
}
public static void IncrementValue()
{
for(int i = 0; i < 1000000; i++)
{
value1++;
Interlocked.Increment(ref value2);
}
}
}
3、Moniter
排他锁
lock其实就是Moniter的语法糖,二者其实就是一样的,Moniter可以更灵活就是了。可以使用TryEnter设置最大等待时间
class Program
{
private static readonly object lock1 = new object();
private static readonly object lock2 = new object();
static void Main(string[] args)
{
Task.Run(LockTooMuch);
lock (lock2)
{
Thread.Sleep(1000); //这里拿到了lock2,而LockTooMuch拿到了lock1
Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5))) //拿不到资源,等5s后就进入else语句了
{
Console.WriteLine("Acquired a protected resource succesfully");
}
else
{
Console.WriteLine("Timeout acquiring a resource!");
}
}
Task.Run(LockTooMuch);
Console.WriteLine("----------------------------------");
lock (lock2) //这里会直接死锁
{
Console.WriteLine("This will be a deadlock!");
Thread.Sleep(1000);
lock (lock1)
{
Console.WriteLine("Acquired a protected resource succesfully");
}
}
}
static void LockTooMuch()
{
lock (lock1)
{
Thread.Sleep(1000);
lock (lock2) { };
}
}
}
4、WaitHandle
参考博客:点这里
这个类是一个抽象类,是用来封装等待对共享资源进行独占访问的操作系统特定的对象。
5、AutoResetEvent
AutoResetEvent表示线程同步事件在一个等待线程释放后收到信号时自动重置。WaitOne()方法阻塞程序执行,Set()方法释放信息。当释放后阻塞的代码继续执行。但下一次执行还需要等待信号。WaitOne()相当于要进门,但是门关了,所以要等Set()方法来开门,WaitOne()下一次来的时候还得等,因为自动调用了ReSet()又把门关上了。
class Program
{
static int num = 0;
private static AutoResetEvent ate1 = new AutoResetEvent(false);
private static AutoResetEvent ate2 = new AutoResetEvent(true); //设为True表示一开始没有设置Reset(),所以第一个WaitOne()没有效果
static void Main(string[] args)
{
Task.Run(Method1);
Task.Run(Method2);
Console.ReadLine();
}
public static void Method1()
{
while (num < 100)
{
ate1.WaitOne();
Console.WriteLine("奇数:"+num++.ToString());
ate2.Set(); //这里自动的调用了.Reset()方法
}
}
public static void Method2()
{
while (num < 100)
{
ate2.WaitOne();
Console.WriteLine("偶数:" + num++.ToString());
ate1.Set();
}
}
}
WaitOne还有一些重载,加一个整数参数表示最大等待时长上限,-1就表示无限等了,返回值为bool型,等到了就是True,没等到就是False。
6、ManualResetEvent
和AutoResetEvent的区别就和它们的名称一样,一个自动Reset(),一个手动Reset()。其他地方完全一样
class Program
{
static int num = 0;
private static ManualResetEvent ate1 = new ManualResetEvent(false);
private static ManualResetEvent ate2 = new ManualResetEvent(true); //设为True表示一开始没有设置Reset(),所以第一个WaitOne()没有效果
static void Main(string[] args)
{
Task.Run(Method1);
Task.Run(Method2);
Console.ReadLine();
}
public static void Method1()
{
while (num < 100)
{
ate1.WaitOne();
Console.WriteLine("奇数:" + num++.ToString());
ate2.Set();
ate2.Reset();
}
}
public static void Method2()
{
while (num < 100)
{
ate2.WaitOne();
Console.WriteLine("偶数:" + num++.ToString());
ate1.Set();
ate1.Reset();
}
}
}
7、Semaphore
其实.NET中的信号量(Semaphore)是操作系统维持的一个整数。当整数位0时。其他线程无法进入。当整数大于0时,线程可以进入。每当一个线程进入,整数-1,线程退出后整数+1。整数不能超过信号量的最大请求数。信号量在初始化的时候可以指定这个整数的初始值。
WaitOne()方法表示当前线程占用信号量,Release() 方法表示当前线程释放信号量
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
Console.ReadLine();
}
}
public class Foo
{
Semaphore semaphore = new Semaphore(0, 3);//允许最大3个线程占用信号量 超过后就会在WaitOne那阻塞
public Foo()
{
for (int i = 0; i < 10; i++)
{
int j = i;
Task.Run(() => DoSomething(j));
}
//semaphore.Release(3);
//Console.WriteLine(semaphore.Release());
//Console.WriteLine(semaphore.Release());
//Console.WriteLine(semaphore.Release());
//Console.WriteLine(semaphore.Release());
}
private void DoSomething(int i)
{
semaphore.WaitOne();//当前线程占用信号量 如果信号量被占满 则阻塞
Console.WriteLine($"{i}进入信号量");
Thread.Sleep(1000);
Console.WriteLine($"{i}释放信号量 信号量线程数量:{semaphore.Release()}");//当前线程释放信号量
}
}
8、SemaphoreSlim
SimaphoreSlim是轻量级的,操作和Semapore是一样的
本质区别是 Semaphore 是基于系统提供的同步原语实现的同步等待,而 SemaphoreSlim 是通过自旋(基于 SpinWait) 实现的同步等待。
Slim 版本在大多数情况下不会出现用户态到系统态的转换,而 Semaphore 则非常可能发生这种情况。但由于 SpinWait 的特点,Slim 版本更加适用于等待时间较短的场景。因为如果出现了长时间的等待(长过一次工作状态的切换),SpinWait 会放弃继续空循环的操作,将执行机会让给其他的线程,这样又会导致上下文的切换。
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
Console.ReadLine();
}
}
public class Foo
{
Semaphore semaphore = new Semaphore(0, 3);//允许最大3个线程占用信号量 超过后就会在WaitOne那阻塞
SemaphoreSlim semaphoreSlim = new SemaphoreSlim(5);
public Foo()
{
for (int i = 0; i < 10; i++)
{
int j = i;
Task.Run(() => DoSomething(j));
}
Thread.Sleep(10000);
//semaphore.Release();
//semaphore.Release(3);
Console.WriteLine(semaphoreSlim.Release());
//Console.WriteLine(semaphore.Release());
//Console.WriteLine(semaphore.Release());
//Console.WriteLine(semaphore.Release());
}
private void DoSomething(int i)
{
// semaphore.WaitOne();//当前线程占用信号量 如果信号量被占满 则阻塞
semaphoreSlim.Wait();
Console.WriteLine($"{i}进入信号量");
Thread.Sleep(1000);
// Console.WriteLine($"{i}释放信号量 信号量线程数量:{semaphore.Release()}");//当前线程释放信号量
Console.WriteLine($"{i}释放信号量 信号量线程数量:{semaphoreSlim.Release()}");//当前线程释放信号量
}
}
9、Mutex
互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它,互斥锁可适用于一个共享资源每次只能被一个线程访问的情况,如果线程获取互斥体,则需要获取该互斥体的第二个线程将挂起,直到第一个线程释放该互斥体。
在Mutex类中,WaitOne()方法用于等待资源被释放,ReleaseMutex()方法用于释放资源。WaitOne()方法在等待ReleaseMutex()方法执行后才会结束。
class Program
{
static Mutex mutex = new Mutex();//互斥锁
static bool hasPay = false;
static void Pay(int num)
{
if (mutex.WaitOne())
{
try
{
if (!hasPay)
{
Console.WriteLine($"编号{num}:已经买单了");
hasPay = true;
}
else
Console.WriteLine($"编号{num}:买单失败");
}
finally
{
mutex.ReleaseMutex();
Console.WriteLine("mutex is Release");
}
}
}
static void Main(string[] args)
{
for (int i = 1; i < 10; i++)
{
int j = i;
Task.Factory.StartNew(()=>Pay(j));
}
Console.ReadLine();
}
}
通过设置Mutex的名称可以做到跨进程,让整个操作系统实现线程同步操作。
标签:同步,Console,C#,void,信号量,static,WriteLine,线程 From: https://www.cnblogs.com/xunzf0402/p/16592331.html