Intern Day85 - 多线程同步/安全问题 - Lock加锁
线程安全
多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时会进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程同步
是指多线程程序中,为了保证后者线程,只有等待前者线程完成之后才能继续执行。就好比买票,前面的人没买到票之前,后面的人必须等待。所谓同步:是指在某一时刻只有一个线程可以访问变量。如果不能确保对变量的访问是同步的,就会产生错误。c#为同步访问变量提供了一个非常简单的方式,即用c#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。
作用和使用场景
作用:锁可以阻止其它线程执行锁块(lock(o){})中的代码,当锁定时,其它线程必须等待锁中的线程执行完成并释放锁。但是这可能会给程序带来性能影响。
使用场景:锁不太适合I/O场景,例如文件I/O,繁杂的计算或者操作比较持久的过程,会给程序带来很大的性能损失。
lock语法
Lock(expression) // expression:你希望跟踪的对象
{
statement_block // 互斥段的代码,这段代码在一个时刻内只可能被一个线程执行
}
lock加锁的对象和原因
对象:
-
加锁主要用于锁住引用型的公共变量,eg:class
-
lock锁定的是一个引用类型,而值类型的变量,如int、struct是不能用lock的
原因:见:https://www.cnblogs.com/tianciliangen/p/4910394.html
lock加锁的注意事项
-
lock只是保证资源不被占用,不会保证执行顺序。 有可能一个线程在循环里连续获得了lock(锁)。所以下面的代码每次运行的时候结果都是不一样的。
-
如果一个类的实例是public的,最好不要lock(this)。因为使用你的类的人也许不知道你用了lock,如果他new了一个实例,并且对这个实例上锁,就很容易造成死锁。(即:如果type是public类型的,不要lock(typeof(type))。)
-
不要去锁一个string,因为程序中任何 跟锁定的string的字符串是一样的 都会被锁定。
具体代码
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
class Program
{
// private object locker = new object();
private static readonly object Locker = new object(); // 这个比上面的语句好
// 设成只读的原因:这时因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了
static int num = 1;
static void Main(string[] args)
{
Program program = new Program();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = 0; i < 4; i++)
{
Thread thread = new Thread(program.Run);
thread.Start();
}
// for (int i = 0; i < 4; i++) wrong
// {
// var t= Task.Run(() => // Task.Run(async () =>
// {
// program.Run();
// });
// t.Start();
// Task.WaitAll(t);
// }
num++;
Console.WriteLine("num = " + num);
Console.WriteLine("主线程ID = " + Thread.CurrentThread.ManagedThreadId.ToString());
stopWatch.Stop();
Console.WriteLine("执行时间 = " + stopWatch.ElapsedMilliseconds + "ms");
Console.ReadKey();
}
public void Run()
{
// 加锁主要用于锁住引用型的公共变量,比如class、string
// 而值类型的变量,如int、struct是不能用锁的
// lock里面尽量少放东西,否则影响效率
lock (Locker) // 这里的就是上面定义的公共变量locker
{
num++;
Console.WriteLine("num = " + num);
Console.WriteLine("子线程ID = " + Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}