加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

C# 多线程中的常见问题

发布时间:2020-12-15 22:40:31 所属栏目:百科 来源:网络整理
导读:1. 资源竞争 当多个同时执行的线程需要同时对全局变量进行写读操作时,容易出现资源竞争的问题,导致运行结果出现多种情况。以下面的例子进行说明: ? private static CancellationTokenSource cs = new CancellationTokenSource(); private static int num

1. 资源竞争

当多个同时执行的线程需要同时对全局变量进行写读操作时,容易出现资源竞争的问题,导致运行结果出现多种情况。以下面的例子进行说明:

?

        private static CancellationTokenSource cs = new CancellationTokenSource();
        private static int num = 5;
        private static object obj = new object();

        static void Main(string[] args)
        {
            Console.WriteLine("Main Start....");
            Task t1 = Task.Factory.StartNew(Test);
            Task t2 = Task.Factory.StartNew(Test);
            Task.WaitAll(new Task[] { t1,t2 });
            cs.Dispose();
            Console.WriteLine("Main end....");
            Console.ReadLine();
        }

        static void Test()
        {
            while (!cs.IsCancellationRequested)    //是否调用Cancel
            {
                Console.WriteLine("TaskId {0} Excute other code....num is {1}",Task.CurrentId,num);
                if(num == 5){
                    Thread.Sleep(50);        //为了方便观察,添加延时
                    num++;                    
                    Console.WriteLine("TaskId {0} and Num is {1}",num);
                    if (!cs.IsCancellationRequested)
                    {
                        cs.Cancel();       //取消操作
                    }
                }
            }
        }

?大多数情况,运行结果如下:

Main Start....
TaskId 1 Excute other code....num is 5
TaskId 2 Excute other code....num is 5
TaskId 2 and Num is 6
TaskId 1 and Num is 7
Main end....

任务1 首先运行至Thread.Sleep(50),等待50ms,cpu开始调度任务2运行至Thread.Sleep(50)。 接着任务1 运行num++,并往控制台输出结果num=6,然后任务2运行num++,并往控制台输出num=7。但有时也会出现下面这种结果:

Main Start....
TaskId 1 Excute other code....num is 5
TaskId 2 Excute other code....num is 5
TaskId 2 and Num is 7
TaskId 1 and Num is 7
Main end....

任务1 首先运行至Thread.Sleep(50),等待50ms,cpu开始调度任务2运行至Thread.Sleep(50)。接着任务1 运行num++,cpu马上开始调度任务2运行num++,并往控制台输出num=7,最后调度任务1往控制台输出num=7。

?解决方式:只需加上线程锁lock,便只会出现第一种运行结果,如下:

        static void Test()
        {
            while (!cs.IsCancellationRequested)    //是否调用Cancel
            {
                Console.WriteLine("TaskId {0} Excute other code....num is {1}",num);
                if(num == 5){
                    Thread.Sleep(50);        //为了方便观察,添加延时
                    lock (obj)               //只有一个线程可以操作
                    {
                        num++;                    
                        Console.WriteLine("TaskId {0} and Num is {1}",num);
                    }
                    if (!cs.IsCancellationRequested)
                    {
                        cs.Cancel();       //取消操作
                    }
                }
            }
        }

?2.线程死锁

至少有2个线程被挂起,等待对方解锁,线程将无限等待下去。

?

        private static int num = 5;
        private static object obj1 = new object();
        private static object obj2 = new object();

        static void Main(string[] args)
        {
            Console.WriteLine("Main Start....");
            Parallel.Invoke(LockTest1,LockTest2);
            Console.WriteLine("Main end....");
            Console.ReadLine();
        }

        static void LockTest1()
        {
            lock(obj1){
                lock(obj2){
                    Console.WriteLine("LockTest1 is running");
                }
            }
        }

        static void LockTest2()
        {
            lock (obj2)
            {
                lock (obj1)
                {
                    Console.WriteLine("LockTest2 is running");
                }
            }
        }

?

运行结果:

Main Start....
LockTest1 is running
LockTest2 is running
Main end....

看似正常,但这段程序在极少数的情况下,会出现死锁。例如CPU先运行LockTest1()中lock(obj1),马上又运行LockTest2()中lock(obj2),这时LockTest1()会等待obj2 解锁,而LockTest2()会等待obj1解锁 ,形成死锁。

?

解决方式:在设计程序时,考虑好锁定的顺序,或者定义锁定超时。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读