首先要避免同步,最好是不要在线程之间共享数据。如果必须要共享数据,就需要用到同步技术。
使用线程容易出现的两个问题:争用条件和死锁。
如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件。
如下案例,执行会出现征用条件的程序断言
public class StateObject
{
private int _state = 5;
public void ChangeState(int loop)
{
if (_state == 5)
{
_state++;
}
Trace.Assert(_state == 6, $"{loop}次循环后出现争用条件");
_state = 5;
}
}
public class SampleTask
{
public void RaceCondition(object o)
{
Trace.Assert(o is StateObject, "o必须是StateObject类型");
StateObject state = o as StateObject;
int i = 0;
while (true)
{
state.ChangeState(i++);
}
}
public void RaceConditions()
{
var state = new StateObject();
for (int i = 0; i < 2; i++)
{
Task.Run(() => new SampleTask().RaceCondition(state));
}
}
}
//调用RaceConditions方法,会出现断言
上述案例,只要在while循环体里面对StateObject 对象加入lock关键字就可以解决争用条件。
死锁:过多的锁定容易出现麻烦。在死锁中,至少有两个线程被挂起,并等待对方解除锁定。由于两个线程都在等待对方,就出现了死锁,线程将无线等待下去。
public class StateObject
{
private int _state = 5;
private object sync = new object();
public void ChangeState(int loop)
{
lock (sync)
{
if (_state == 5)
{
_state++;
}
Trace.Assert(_state == 6, $"{loop}次循环后出现争用条件");
}
_state = 5;
}
}
public class SampleTask
{
private StateObject _s1;
private StateObject _s2;
public SampleTask(StateObject s1, StateObject s2)
{
_s1 = s1;
_s2 = s2;
}
public void Deadlock1()
{
int i = 0;
while (true)
{
lock (_s1)
{
lock (_s2)
{
_s1.ChangeState(i);
_s2.ChangeState(i++);
Console.WriteLine($"still running, {i}");
}
}
}
}
public void Deadlock2()
{
int i = 0;
while (true)
{
lock (_s2)
{
lock (_s1)
{
_s1.ChangeState(i);
_s2.ChangeState(i++);
Console.WriteLine($"still running, {i}");
}
}
}
}
//public void RaceCondition(object o)
//{
// Trace.Assert(o is StateObject, "o必须是StateObject类型");
// StateObject state = o as StateObject;
// int i = 0;
// while (true)
// {
// state.ChangeState(i++);
// }
//}
//public void RaceConditions()
//{
// var state = new StateObject();
// for (int i = 0; i < 2; i++)
// {
// Task.Run(() => new SampleTask().RaceCondition(state));
// }
//}
}
//调用
var state1 = new StateObject();
var state2 = new StateObject();
new Task(new SampleTask(state1,state2).Deadlock1).Start();
new Task(new SampleTask(state1, state2).Deadlock2).Start();
Console.ReadLine();
lock语句和线程安全:
public class SharedState
{
//public int State { get; set; }
private int _state = 0;
private object _syncRoot = new object();
public int State => _state;
public int IncrementState()
{
lock (_syncRoot)
{
return ++_state;
}
}
}
public class Job
{
private SharedState _sharedState;
public Job(SharedState sharedState)
{
_sharedState = sharedState;
}
public void DoTheJob()
{
for (int i = 0; i < 50000; i++)
{
_sharedState.IncrementState();
}
}
}
//调用方式
int numTasks = 20;
var state = new SharedState();
var tasks = new Task[numTasks];
for (int i = 0; i < numTasks; i++)
{
tasks[i] = Task.Run(() => new Job(state).DoTheJob());
}
Task.WaitAll(tasks);
Console.WriteLine($"summarized {state.State}");
Console.ReadLine();
Interlocked类用于使变量的简单语句原子化,Interlocked类提供了以线程安全的方式递增、递减、交换和读取值的方法。
Interlocked在处理简单的原子化语句性能优于lock语句;看如下案例(递减:Decrement;交换:Exchange;读取:Read)
public int IncrementState()
{
//lock (_syncRoot)
//{
// return _state++;
return Interlocked.Increment(ref _state);
//}
}
public void DoTheJob()
{
for (int i = 0; i < 50000000; i++)
{
_sharedState.IncrementState();
}
}
//测试
Stopwatch sw = new Stopwatch();
sw.Start();
int numTasks = 20;
var state = new SharedState();
var tasks = new Task[numTasks];
for (int i = 0; i < numTasks; i++)
{
tasks[i] = Task.Run(() => new Job(state).DoTheJob());
}
Task.WaitAll(tasks);
sw.Stop();
Console.WriteLine($"summarized {state.State}");
Console.WriteLine("所用时间:" + sw.Elapsed.TotalSeconds+"秒");
Console.ReadLine();
下章讲述后续同步类的使用