作者:莹TX_937 | 来源:互联网 | 2023-09-17 18:15
1.本节将描述如何确保当一个线程使用某些资源时,同时其他线程无法使用该资源。usingSystem;usingSystem.Collections.Generic;usingSys
1. 本节将描述如何确保当一个线程使用某些资源时,同时其他线程无法使用该资源。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static System.Console;
using static System.Threading.Thread;
namespace ConsoleApp1
{
class Program
{
///
/// 当主程序启动时,创建了一个Counter类的对象。该类定义了一个可以递增和递减的简单的计数器。
///
///
static void Main(string[] args)
{
WriteLine("Incorrect counter");
var c = new Counter();
var t1 = new Thread(() => TestCounter(c));
var t2 = new Thread(() => TestCounter(c));
var t3 = new Thread(() => TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
WriteLine($"Total count:{c.Count}");
WriteLine("------------------------------------");
WriteLine("Correct counter");
var c1 = new CounterWithLock();
t1 = new Thread(() => TestCounter(c1));
t2 = new Thread(() => TestCounter(c1));
t3 = new Thread(() => TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
WriteLine($"Total count:{c1.Count}");
}
static void TestCounter(CounterBase c)
{
for (int i = 0; i <100000; i++)
{
c.Increment();
c.Decrement();
}
}
}
///
/// 该类定义了一个可以递增和递减的简单的计数器。
///
class Counter : CounterBase
{
public int Count { get; private set; }
public override void Decrement()
{
Count--;
}
public override void Increment()
{
Count++;
}
}
class CounterWithLock : CounterBase
{
private readonly object _syncRoot=new object();
public int Count { get; private set; }
public override void Decrement()
{
lock (_syncRoot)
{
Count--;
}
}
public override void Increment()
{
lock (_syncRoot)
{
Count++;
}
}
}
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
}
2 工作原理
当主程序启动时,创建了一个Counter类的对象。该类定义了一个可以递增和递减的简单的计数器。然后我们启动了三个线程。这三个线程共享同一个counter示例,在一个周期中进行一次递增和一次递减。这将导致不确定的情况。如果运行程序多次,则会打印出多个不同的计数器的值。结果可能是0,单大多数情况下则不是0。
这是因为Counter类并不是线程安全的。当多个线程同时访问counter对象时,第一个线程得到的counter值10并增加为11.然后第二个线程得到的值是11并增加为12。第一个线程得到counter值12,但是递减操作发生前,第二个线程得到的counter值也是12.然后第一个线程将12递减为11并保存回counter中,同时第二个线程进行了同样的操作。结果我们进行了两次递增操作但是只有一次递减操作,这显然不对。这种情形被称为竞争条件(racecondition)。竞争条件是多线程环境中非常常见的导致错误的原因。
为了确保不会发生以上情形,必须保证当有线程操作counter对象时,所有其他线程必须等待直到当前线程完成操作。我们可以使用lock关键字来实现这种行为。如果锁定了一个对象,需要访问该对象的所有其他线程则会处于阻塞状态,并等待直到该对象解除锁定。这可能会导致严重的性能问题。