非线程安全是指多线程操作用一个对象可能出现问题。
线程安全就是多线程访问时,采用加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到这个线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能多个线程先后更改数据造成所得到的数据是脏数据。
看下面这个例子:
static int num = 0;
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for(int i = 0; i < 5; i++)
{
Thread thread = new Thread(Run);
thread.Start();
}
num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Main thread ID is:"+Thread.CurrentThread.ManagedThreadId);
sw.Stop();
Console.ReadKey();
}
static void Run()
{
num++;
Console.WriteLine("num的值是:" + num);
Console.WriteLine("child thread's id is " + Thread.CurrentThread.ManagedThreadId);
}
结果:
多次运行发现num 的值有时并不是连续递增的,输出也是没有顺序的,每次输出的值都不相同,这是因为异步线程同时访问一个成员时造成,这样的多线程对我们来说是不可控的。
这就是典型非线程安全问题,解决这个问题要用线程同步。线程同步方法很多,
下面说第一种。
- 使用Thread.Join()方法,这个方法是阻塞调用线程,直到某个线程终止时为止。我们把它加到代码中看看效果。还是上面那个例子,我们在主方法中加入Join方法。
static int num = 0;
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for(int i = 0; i < 5; i++)
{
Thread thread = new Thread(Run);
thread.Join();
thread.Start();
}
num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Main thread ID is:"+Thread.CurrentThread.ManagedThreadId);
sw.Stop();
Console.ReadKey();
}
static void Run()
{
num++;
Console.WriteLine("num的值是:" + num);
Console.WriteLine("child thread's id is " + Thread.CurrentThread.ManagedThreadId);
}
再看运行结果
num现在是连续递增的且输出有序了。main方法其实就是一个主线程,我们在主线程中调用另外一个线程,此时的主线程被称为调用线程,被调用的线程,被称为被调用线程。在主线程(主方法)中写入thread.join()方法,运行时主线程被阻塞,要等到被调用线程执行完后,才继续执行。
- 使用lock
lock语法
private static object ojb = new object();
lock(obj)
{
//锁定运行的代码段
}
常用的做法是创建一个object对象,并且永不复制,应lock一个不影响其他操作的的私有对象。
示例:
private static object locker = new object(); //创建一个object对象
static int num = 0;
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for(int i = 0; i < 5; i++)
{
Thread thread = new Thread(Run);
thread.Start();
//thread.Join();//用Join()方法实现线程同步
}
num++;
Console.WriteLine("num is:" + num);
Console.WriteLine("Main thread ID is:"+Thread.CurrentThread.ManagedThreadId);
sw.Stop();
Console.ReadKey();
}
static void Run()
{
lock(locker)
{
num++;
Console.WriteLine("num的值是:" + num);
Console.WriteLine("child thread's id is " + Thread.CurrentThread.ManagedThreadId);
}
}
num也是有序递增的。