浏览器标题切换
浏览器标题切换end

Intern Day85 - 多线程同步/安全问题 - Lock加锁

线程安全

多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时会进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

线程同步

是指多线程程序中,为了保证后者线程,只有等待前者线程完成之后才能继续执行。就好比买票,前面的人没买到票之前,后面的人必须等待。所谓同步:是指在某一时刻只有一个线程可以访问变量。如果不能确保对变量的访问是同步的,就会产生错误。c#为同步访问变量提供了一个非常简单的方式,即用c#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。

作用和使用场景

作用:锁可以阻止其它线程执行锁块(lock(o){})中的代码,当锁定时,其它线程必须等待锁中的线程执行完成并释放锁。但是这可能会给程序带来性能影响。

使用场景:锁不太适合I/O场景,例如文件I/O,繁杂的计算或者操作比较持久的过程,会给程序带来很大的性能损失。

lock语法

Lock(expression) // expression:你希望跟踪的对象
{
    statement_block // 互斥段的代码,这段代码在一个时刻内只可能被一个线程执行
}

lock加锁的对象和原因

对象:

  1. 加锁主要用于锁住引用型的公共变量,eg:class

  2. lock锁定的是一个引用类型,而值类型的变量,如int、struct是不能用lock的

原因:见:https://www.cnblogs.com/tianciliangen/p/4910394.html

lock加锁的注意事项

  1. lock只是保证资源不被占用,不会保证执行顺序。 有可能一个线程在循环里连续获得了lock(锁)。所以下面的代码每次运行的时候结果都是不一样的。

  2. 如果一个类的实例是public的,最好不要lock(this)。因为使用你的类的人也许不知道你用了lock,如果他new了一个实例,并且对这个实例上锁,就很容易造成死锁。(即:如果type是public类型的,不要lock(typeof(type))。)

  3. 不要去锁一个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());
        } 
    }
}
posted @ 2021-05-13 16:23  抓水母的派大星  阅读(104)  评论(0编辑  收藏  举报