c#线程安全

先看一个例子:

复制代码
   /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");
            for (int i = 0; i < 5; i++)
            {
                Task.Run(() =>
                { 
                    Console.WriteLine($"This is {i}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                    Thread.Sleep(2000); 
                    Console.WriteLine($"This is {i}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                });
            }

            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 结果:

复制代码
*** **   btnSafe_Click start -主线程ID:1-2021-09-18 08:44:36***** ***
This is 1  start -3 
This is 3  start -4 
This is 4  start -7 
This is 5  start -6 
This is 5  start -9 
*** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 08:44:36********** *
This is 5  end -7 
This is 5  end -6 
This is 5  end -9 
This is 5  end -4 
This is 5  end -3 
复制代码

 

从结果我们可以看出来多个线程中的i值在实际执行中是相同的,都是5,按照正常逻辑应该是0 1 2 3 4这4个值,为什么会出现这种情况呢?首先,必须要知道线程是属于操作系统的,操作系统根据调度策略来分配线程,不是实时响应的,可能等到i已经循环到5了才开始执行这个线程。

我们可以做一下更改,设置一个局部变量,这样每循环一次都会产生一个新的局部变量值,互不干扰。

复制代码
        /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    Console.WriteLine($"This is {i} -- {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                    Thread.Sleep(2000);
                    Console.WriteLine($"This is {i} -- {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                });
            }

            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

结果:

复制代码
*** **   btnSafe_Click start -主线程ID:1-2021-09-18 09:05:25***** ***
This is 0 -- 0  start -3 
This is 5 -- 1  start -5 
*** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 09:05:25********** *
This is 5 -- 4  start -8 
This is 5 -- 3  start -7 
This is 5 -- 2  start -6 
This is 5 -- 4  end -8 
This is 5 -- 0  end -3 
This is 5 -- 2  end -6 
This is 5 -- 3  end -7 
This is 5 -- 1  end -5 
复制代码

 

案例2:

多线程同时访问一个集合一般是没什么问题的,但是线程安全问题一般是出现在修改一个对象的时候。

多线程安全定义:一段代码,单线程执行和多线程执行的结果不一致,就表明有线程安全问题。

复制代码
        /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***"); 
            #region MyRegion
            List<int> intList = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    intList.Add(i); 
                });
            }

            Thread.Sleep(5000);
            Console.WriteLine(intList.Count);
            #endregion
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

结果:

*** **   btnSafe_Click start -主线程ID:1-2021-09-18 09:32:12***** ***
9997
*** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 09:32:17********** *

 

结果的长度是9997,小于10000,其实下一次执行的时候结果可能就变成另一个了,这都是不确定的,上面的代码是想让每一个线程都单独添加一个数,比如说线程1添加数0,线程2添加数1,,,,,知道10000,。但是线程的请求不是实时的,导致最终会导致intList中存在大量的重复数据。数组在内存中是连续存在的, 如果在同一时刻上同时由多个线程同时在这个内存位置上做更改,就会出现覆盖,所以存在数据丢失。

解决数据丢失问题:

复制代码
    private static readonly object LOCK = new object();
 
  /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***"); 
            #region MyRegion
            List<int> intList = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                int k = i;
                Task.Run(() =>
                { 
                    lock(LOCK)
                    {
                        intList.Add(i); 
                    }
                     
                });
            }

            Thread.Sleep(5000);
            Console.WriteLine(intList.Count);
            #endregion
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

 

加了lock就保证每次lock方法体中每一次都只能有一个线程进入,此时intList数量为10000。但是还是解决不了数据重复的问题。要解决数据重复的话就做一个局部变量,和第一个案例一样就可以了。

Lock原理:Lock其实是一个语法糖,等价于Monitor,是锁定一个内存引用地址,对这个引用对象做了一个状态标识,但是不能锁定值类型以及null。等价于下面的写法:

复制代码
try
{    
    Monitor.Enter(obj) 
}
catch()
{}
finally
{
      Monitor.Exit(obj) 
}
复制代码

 

案例3:Lock相关问题

如果使用共同的一个变量来进行锁定:

复制代码
        /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");
            TestLock.Show();
            #region MyRegion
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock (TestLock.LOCK)
                    {
                        Console.WriteLine($" MainShow This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" MainShow This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                    }
                });
            }
            #endregion 
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码
复制代码
    /// <summary>
    /// 测试Lock类
    /// </summary>
    public class TestLock
    {
        public static readonly object LOCK = new object();
        public static void Show()
        {
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock (LOCK)
                    {
                        Console.WriteLine($" TestLockShow This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" TestLockShow This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                    }
                });
            }
        }

    }
复制代码

 

结果:

复制代码
*** **   btnSafe_Click start -主线程ID:1-2021-09-18 10:43:18***** ***
 TestLockShow This is  0  start -13 
*** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 10:43:18********** *
 TestLockShow This is  0  end -13 
 TestLockShow This is  1  start -22 
 TestLockShow This is  1  end -22 
 MainShow This is  4  start -13 
 MainShow This is  4  end -13 
 TestLockShow This is  3  start -24 
 TestLockShow This is  3  end -24 
 TestLockShow This is  2  start -23 
 TestLockShow This is  2  end -23 
 TestLockShow This is  4  start -25 
 TestLockShow This is  4  end -25 
 MainShow This is  0  start -26 
 MainShow This is  0  end -26 
 MainShow This is  1  start -27 
 MainShow This is  1  end -27 
 MainShow This is  2  start -28 
 MainShow This is  2  end -28 
 MainShow This is  3  start -29 
 MainShow This is  3  end -29 
复制代码

 

通过结果可以看出来这是同步执行的因为不管是执行TestLockShow还是MainShow,必须是同一个线程进入这个区域之后开始结束是连续的,2个方法没有混起来,所以共用一个相同的锁变量就会出现相互阻塞。

如果更改代码,锁变量不共用:

复制代码
     /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");
            TestLock.Show();
            #region MyRegion
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock ( LOCK)
                    {
                        Console.WriteLine($" MainShow This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" MainShow This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                    }
                });
            }
            #endregion 
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

结果:

 

 可以看出来此时是并发执行的。所以最好不要共用。

案例3:锁变量如果不是静态变量会如何?

代码:

复制代码
  /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");
            TestLock.Show();
            {
                TestLock testLock = new TestLock();
                testLock.ShowTemp(1);

                TestLock testLock1 = new TestLock();
                testLock1.ShowTemp(2);
            }
              
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

测试类:

复制代码
 /// <summary>
    /// 测试Lock类
    /// </summary>
    public class TestLock
    {
        private static readonly object LOCK = new object();
        public static void Show()
        {
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock (LOCK)
                    {
                        Console.WriteLine($" TestLockShow This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" TestLockShow This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                    }
                });
            }
        }

        private   readonly object LOCKTemp = new object();
        public void ShowTemp(int index)
        {
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock (LOCKTemp)
                    {
                        Console.WriteLine($"index={index}- ShowTemp This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" index={index}- ShowTemp This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                    }
                });
            }
        }

    }
复制代码

 

结果:

复制代码
*** **   btnSafe_Click start -主线程ID:1-2021-09-18 12:00:47***** ***
 
*** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 12:00:47********** *
 TestLockShow This is  0  start -7 
index=1- ShowTemp This is  1  start -4 
 TestLockShow This is  0  end -7 
 TestLockShow This is  3  start -3 
index=2- ShowTemp This is  0  start -7 
 index=1- ShowTemp This is  1  end -4 
index=1- ShowTemp This is  0  start -6 
 TestLockShow This is  3  end -3 
 TestLockShow This is  4  start -5 
 index=2- ShowTemp This is  0  end -7 
index=2- ShowTemp This is  1  start -4 
 index=1- ShowTemp This is  0  end -6 
index=1- ShowTemp This is  2  start -10 
 TestLockShow This is  4  end -5 
 TestLockShow This is  1  start -8 
 index=2- ShowTemp This is  1  end -4 
index=2- ShowTemp This is  4  start -3 
 index=1- ShowTemp This is  2  end -10 
index=1- ShowTemp This is  3  start -11 
 TestLockShow This is  1  end -8 
 TestLockShow This is  2  start -9 
 index=2- ShowTemp This is  4  end -3 
index=2- ShowTemp This is  2  start -13 
 index=1- ShowTemp This is  3  end -11 
index=1- ShowTemp This is  4  start -12 
 TestLockShow This is  2  end -9 
 index=2- ShowTemp This is  2  end -13 
index=2- ShowTemp This is  3  start -14 
 index=1- ShowTemp This is  4  end -12 
 index=2- ShowTemp This is  3  end -14 
复制代码

 

可以看出来是并发执行的,原因就在于非静态成员变量在new类对象的时候都会重新初始化一次,这2次调用,锁变量就不是同一个值了,在堆中存的地址也不一样了。所以最好设置为静态变量。

案例3:锁变量为string类型

string的不变性和驻留性

代码:

复制代码
       private readonly string LockString = "哈哈";
        /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");
            TestLock.Show();
            {
                TestLock testLock = new TestLock();
                testLock.ShowString(1);
                for (int i = 0; i < 5; i++)
                {
                    int k = i;
                    Task.Run(() =>
                    {
                        lock (LockString)
                        {
                            Console.WriteLine($" MainShow This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                            Thread.Sleep(2000);
                            Console.WriteLine($" MainShow This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        }
                    });
                }
            }
 
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

测试类:

复制代码
  /// <summary>
    /// 测试Lock类
    /// </summary>
    public class TestLock
    {  private readonly string LockString  = "哈哈";
        public void ShowString(int index)
        {
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock (LockString)
                    {
                        Console.WriteLine($"index={index}- ShowString This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" index={index}- ShowString This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                    }
                });
            }
        }

    }
复制代码

 

  结果:

复制代码
 **   btnSafe_Click start -主线程ID:1-2021-09-18 15:22:07***** *** *** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 15:22:07********** *
 ShowString This is  0  start -3 
  ShowString This is  0  end -3 
 MainShow This is  0  start -4 
 MainShow This is  0  end -4 
 ShowString This is  2  start -5 
  ShowString This is  2  end -5 
 MainShow This is  2  start -6 
 MainShow This is  2  end -6 
 ShowString This is  1  start -7 
  ShowString This is  1  end -7 
 ShowString This is  4  start -8 
  ShowString This is  4  end -8 
 ShowString This is  3  start -9 
  ShowString This is  3  end -9 
 MainShow This is  1  start -10 
 MainShow This is  1  end -10 
 MainShow This is  3  start -11 
 MainShow This is  3  end -11 
 MainShow This is  4  start -12 
 MainShow This is  4  end -12 
复制代码

 

因为锁定的是内存引用,字符串是享元的,这2个字符串都是相同的值,虽然在栈上的地址不一样但是在堆中的地址是一样的,所以相当于锁的都是同一个,所以是不能并发的。

CLR中维护着一个驻留池(Intern Pool)的散列表(HashTable),这个表记录了所有在代码中使用字面量声明的字符串实例的引用 ,使用字面量声明的字符串都会被记录到散驻留池(散列表 键为字符串 值为字符串存储地址),

如:string str="abc"; 或 string str="a"+"bc"这种就可以称为字面量

但是 string str=变量+变量 或者 变量+字符串 这种都不能称为字面量

比如前后2个字符串的内容一致,那么在堆中的地址是一样的,这样就是同一个引用了。最好不要用。

复制代码
    /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");

            {
TestLockGeneric
<int>.Show(1); TestLockGeneric<int>.Show(2); TestLockGeneric<TestLock>.Show(3); } Console.WriteLine($"*** **** btnSafe_Click end -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *"); }
复制代码

 

测试类:

复制代码
    /// <summary>
    /// 测试Lock类
    /// </summary>
    public class TestLockGeneric<T>
    {
        private static readonly object LOCK = new object();
        public static void Show(int index)
        {
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    lock (LOCK)
                    {
                        Console.WriteLine($" TestLockGeneric This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($" TestLockGeneric This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                    }
                });
            }
        } 

    }
复制代码

 

结果:

复制代码
*** **   btnSafe_Click start -主线程ID:1-2021-09-18 16:00:32***** ***  TestLockGeneric This is  0  start -3 
*** ****  btnSafe_Click end  -主线程ID:1-2021-09-18 16:00:33********** *
 TestLockGeneric This is  0  end -3 
 TestLockGeneric This is  4  start -4 
 TestLockGeneric This is  0  start -3 
 TestLockGeneric This is  4  end -4 
 TestLockGeneric This is  1  start -5 
 TestLockGeneric This is  0  end -3 
 TestLockGeneric This is  1  start -13 
 TestLockGeneric This is  1  end -5 
 TestLockGeneric This is  2  start -6 
 TestLockGeneric This is  1  end -13 
 TestLockGeneric This is  4  start -3 
 TestLockGeneric This is  2  end -6 
 TestLockGeneric This is  0  start -7 
 TestLockGeneric This is  4  end -3 
 TestLockGeneric This is  3  start -4 
 TestLockGeneric This is  0  end -7 
 TestLockGeneric This is  3  start -8 
 TestLockGeneric This is  3  end -4 
 TestLockGeneric This is  2  start -14 
 TestLockGeneric This is  3  end -8 
 TestLockGeneric This is  2  start -9 
 TestLockGeneric This is  2  end -14 
 TestLockGeneric This is  2  end -9 
 TestLockGeneric This is  1  start -10 
 TestLockGeneric This is  1  end -10 
 TestLockGeneric This is  3  start -11 
 TestLockGeneric This is  3  end -11 
 TestLockGeneric This is  4  start -12 
 TestLockGeneric This is  4  end -12 
复制代码

 

因为泛型类在类型参数相同时候是同一类,参数类型不同时候就不是同一类,所以1与2是不并发,1与3是并发的。

 

案例4:Lock(this)

代码:

复制代码
        /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");

            {
                TestLock testLock = new TestLock();
                testLock.ShowThis(1);

                TestLock testLock2 = new TestLock();
                testLock2.ShowThis(2);
            }
 
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

测试类:

复制代码
    /// <summary>
    /// 测试Lock类
    /// </summary>
    public class TestLock
    {
        public void ShowThis(int index)
        {
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                Task.Run(() =>
                {
                    //this是当前实例
                    lock (this)
                    {
                        Console.WriteLine($" ShowThis This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        Thread.Sleep(2000);
                        Console.WriteLine($"  ShowThis This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");

                    }
                });
            }
        } 

    }
复制代码

 

这个不用看就知道肯定是会出现并发,因为this是当前实例,没法判断。

还有一种写法:

复制代码
        /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");

            {
              TestLock testLock = new TestLock();
                testLock.ShowThis(1);
                for (int i = 0; i < 5; i++)
                {
                    int k = i;
                    Task.Run(() =>
                    {
                        lock (testLock)
                        {
                            Console.WriteLine($" MainShow This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                            Thread.Sleep(2000);
                            Console.WriteLine($" MainShow This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                        }
                    });
                }
            }
 
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
复制代码

 

这种肯定是不会并发的,因为ShowThis中的用的是lock(this),和 lock (testLock)中的testLock是同一个实例,而且都是引用类型。

案例5:Lock(this) 的死锁问题

代码:

复制代码
  /// <summary>
        /// 多线程安全
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSafe_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"*** **   btnSafe_Click start -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}***** ***");

            {
                TestLock testLock = new TestLock();
                testLock.ShowThisAnother(1);
                 
            }
 
            Console.WriteLine($"*** ****  btnSafe_Click end  -主线程ID:{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}********** *");
        }
    }
复制代码

 

 

测试类:

复制代码
    /// <summary>
    /// 测试Lock类
    /// </summary>
    public class TestLock
    {
        private int _Num = 0;
        public void ShowThisAnother(int index)
        {
            this._Num++;
            for (int i = 0; i < 5; i++)
            {
                int k = i;
                //this是当前实例
                lock (this)
                {
                    Console.WriteLine($" ShowThis This is  {k}  start -{Thread.CurrentThread.ManagedThreadId.ToString()} ");
                    Thread.Sleep(1000);
                    Console.WriteLine($"  ShowThis This is  {k}  end -{Thread.CurrentThread.ManagedThreadId.ToString()} "); 
                    if (this._Num < 5)
                    {
                        this.ShowThisAnother(index);
                    }
                    else
                    {
                        break;
                    }
                }

            }
        } 

    }
复制代码

 

结果:不会死锁。

死锁概念

只所以不会死锁是因为lock(this)中的this是当前实例,  if (this._Num < 5)的时候再次调用ShowThisAnother这个方法,此时的this和上一次的this就不同了,所以不会形成死锁。因为此时锁不住,第二次调这个方法的时候是可以直接进行lock方法体中的,如下图,这里的this中包含了3个参数,其中没调一次这个方法,_Num都会递增,所以每次的this都不同,但是如果去掉这个_Num成员变量,就会发生死锁,因为不管调用多少次,LOCKTemp和LockString的值都不变,都是一样的值,所以形成了死锁。

 

posted @   安静点--  阅读(536)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示