好记性不如烂笔头,方便以后忘记了来这里看看,还有什么没有提到的大家可以告诉我,帮忙完善,谢谢!~ Donate?[1]
1.2.1 lock关键字
使用lock关键字来确保当一个线程使用某些资源时,同时其他线程无法使用该资源.
class Counter
{
private readonly object _syncRoot = new object();
public int Count { get; private set; }
public void Add()
{
lock (_syncRoot)
{
Count++;
}
}
public void Sub()
{
lock (_syncRoot)
{
Count--;
}
}
}
var c = new Counter();
//创建多个线程来访问c
Thread t1 = new Thread(()=> TestCounter(c));
Thread t2 = new Thread(() => TestCounter(c));
Thread t3 = new Thread(() => TestCounter(c));
Thread t4 = new Thread(() => TestCounter(c));
Thread t5 = new Thread(() => TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t4.Start();
t5.Start();
t1.Join();
t2.Join();
t3.Join();
t4.Join();
t5.Join();
Console.WriteLine("count:"+c.Count);
最后输出count:0,有人可能会说这不是很正常吗,那请自己动手把Counter类中lock部分都移除再试一遍,最后输出就会不一样了!~
变成这样:
public void Add()
{
Count++;
}
public void Sub()
{
Count--;
}
这是因为如果去掉lock部分,Counter类就不是线程安全的.
当多个线程同时访问counter对象时,第一个线程得到的counter值是10并增加为11,
第二个线程得到的值是11并增加为12,
第一个线程得到counter值12,但递减操作发生前,
第二个线程得到counter值也是12,
第一个线程将12递减为11并存回counter中,
同时第二个线程进行了同样的操作.
结果我们进行了两次两次递增操作但只有第一递减操作,
这显然是不对的.
这种情形被称为竞争条件(race condition),竞争条件是多线程环境中导致错误的常见原因.
为了确保不会发生以上情形,必须保证当有线程操作counter对象时,所有其他线程必须等待直到当前线程完成操作.我们可以使用lock关键字来实现这种行为.如果锁定了一个对象,需要访问该对象的所有其他线程则会处于阻塞状态,并等待知道该对象解除锁定,但这可能会导致严重的性能问题.
1.2.2 使用Monitor类锁定资源
死锁(deadlock)也是多线程编程中常见的错误.由于死锁将导致程序停止工作,可以使用Monitor类来避免死锁.
class Program
{
static void Main(string[] args)
{
object l1 = new object();
object l2 = new object();
new Thread(()=> LockObject(l1,l2)).Start();
lock (l2)
{
Console.WriteLine("Monitor.TryEnter 可以避免死锁,在超时后返回false");
Thread.Sleep(1000);
if (Monitor.TryEnter(l1,TimeSpan.FromSeconds(5)))
{
Console.WriteLine("成功获取到一个受保护的资源!");
}
else
{
Console.WriteLine("获取资源超时!");
}
}
Console.WriteLine("------------------------------------");
new Thread(() => LockObject(l1, l2)).Start();
lock (l2)
{
Console.WriteLine("这将是一个死锁!");
Thread.Sleep(1000);
lock (l1)
{
Console.WriteLine("成功获取到一个受保护的资源!");
}
}
Console.WriteLine("End.");
Console.ReadKey();
}
static void LockObject(object l1,object l2)
{
lock (l1)
{
Thread.Sleep(1000);
lock (l2);
}
}
}
1.2.3 处理异常
在线程中始终使用try/catch代码块是非常重要的,因为不可能在线程代码之外来捕获异常.
class Program
{
static void Main(string[] args)
{
var t = new Thread(ThreadOne);
t.Start();
t.Join();
try
{
t = new Thread(ThreadTwo);
t.Start();
}
catch (Exception ex)
{
Console.WriteLine($"Main:{ex.Message}");
}
}
static void ThreadOne()
{
try
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(1000);
throw new Exception("Boom!!!");
}
catch (Exception ex)
{
Console.WriteLine($"ThreadOne:{ex.Message}");
}
}
static void ThreadTwo()
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(1000);
throw new Exception("Boom!!!");
}
}
由上图可见ThreadOne的异常被捕获,但是ThreadTwo的异常未被捕获
-
赞赏支持 ↩