博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程、任务和同步学习笔记(五)
阅读量:7245 次
发布时间:2019-06-29

本文共 10923 字,大约阅读时间需要 36 分钟。

1、如果两个或多个线程访问相同的对象,或者访问不同步的共享状态,会出现争用条件。

1 using System; 2 using System.Text; 3 using System.Threading; 4  5 class Outputer 6 { 7     public void Output(string msg) 8     { 9         for (int i = 0; i < msg.Length; i++)10         {11             Console.Write(msg[i]);12         }13         Console.WriteLine();14     }15 }16 17 class Program18 {19     static void Main(string[] args)20     {21         Outputer outputer = new Outputer();22         object locker = new object();23         StringBuilder str = new StringBuilder();24         for (int i = 0; i < 26; i++)25         {26             str.Append(((char)('A' + i)).ToString());27         }28         new Thread((msg) =>29         {30             while (true)31             {32                 outputer.Output(msg.ToString());33             }34         }).Start(str.ToString());35         new Thread(() =>36         {37             while (true)38             {39                 outputer.Output("1234567890");40             }41         }).Start();42     }43 }

运行结果:

2、要避免该问题,可以使用lock语句锁定共享的对象。

1 using System; 2 using System.Text; 3 using System.Threading; 4  5 class Outputer 6 { 7     public void Output(string msg) 8     { 9         for (int i = 0; i < msg.Length; i++)10         {11             Console.Write(msg[i]);12         }13         Console.WriteLine();14     }15 }16 17 class Program18 {19     static void Main(string[] args)20     {21         Outputer outputer = new Outputer();22         object locker = new object();23         StringBuilder str = new StringBuilder();24         for (int i = 0; i < 26; i++)25         {26             str.Append(((char)('A' + i)).ToString());27         }28         new Thread((msg) =>29         {30             while (true)31             {32                 lock (locker)33                 {34                     outputer.Output(msg.ToString());35                 }36             }37         }).Start(str.ToString());38         new Thread(() =>39         {40             while (true)41             {42                 lock (locker)43                 {44                     outputer.Output("1234567890");45                 }46             }47         }).Start();48     }49 }

运行结果:

3、也可以将共享对象设置为线程安全的对象。

1 using System; 2 using System.Text; 3 using System.Threading; 4  5 class Outputer 6 { 7     public void Output(string msg) 8     { 9         lock (this)10         {11             for (int i = 0; i < msg.Length; i++)12             {13                 Console.Write(msg[i]);14             }15             Console.WriteLine();16         }17     }18 }19 20 class Program21 {22     static void Main(string[] args)23     {24         Outputer outputer = new Outputer();25         object locker = new object();26         StringBuilder str = new StringBuilder();27         for (int i = 0; i < 26; i++)28         {29             str.Append(((char)('A' + i)).ToString());30         }31         new Thread((msg) =>32         {33             while (true)34             {35                 outputer.Output(msg.ToString());36             }37         }).Start(str.ToString());38         new Thread(() =>39         {40             while (true)41             {42                 outputer.Output("1234567890");43             }44         }).Start();45     }46 }

4、过多的锁定会造成死锁。所谓死锁即是至少有两个线程被挂起,互相等待对方解锁,以至于线程无限等待下去。

1 using System; 2 using System.Threading; 3  4 class DeadLocker 5 { 6     object locker1 = new object(); 7     object locker2 = new object(); 8  9     public void Method1()10     {11         while (true)12         {13             lock (locker1)14             {15                 lock (locker2)16                 {17                     Console.WriteLine("First lock1, and then lock2");18                 }19             }20         }21     }22 23     public void Method2()24     {25         while (true)26         {27             lock (locker2)28             {29                 lock (locker1)30                 {31                     Console.WriteLine("First lock2, and then lock1");32                 }33             }34         }35     }36 }37 38 class Program39 {40     static void Main(string[] args)41     {42         DeadLocker dl = new DeadLocker();43         new Thread(dl.Method1).Start();44         new Thread(dl.Method2).Start();45     }46 }

运行结果:

5、同步问题和争用条件以及死锁相关,要避免同步问题,最好就不要在线程之间共享数据。如果要共享数据就必须使用同步技术,确保一次只有一个线程访问和改变共享状态。在C#中,lock语句是设置锁定和解除锁定的一种简单方式。编译器将其编译为IL后,会被编译成了调用Monitor类的Enter和Exit方法。

1 using System; 2 using System.Threading; 3  4 class Program 5 { 6     static void Main(string[] args) 7     { 8     } 9 10     void Method()11     {12         lock (typeof(Program))13         {14         }15     }16 }

编译结果:

6、争用条件的另一个例子。

1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4  5 class SharedState 6 { 7     public int State { get; set; } 8 } 9 10 class Worker11 {12     SharedState state;13 14     public Worker(SharedState state)15     {16         this.state = state;17     }18 19     public void DoJob()20     {21         for (int i = 0; i < 500; i++)22         {23             state.State += 1;24         }25     }26 }27 28 class Program29 {30     static void Main(string[] args)31     {32         int numTasks = 20;33         var state = new SharedState();34         var tasks = new Task[numTasks];35         for (int i = 0; i < numTasks; i++)36         {37             tasks[i] = new Task(new Worker(state).DoJob);38             tasks[i].Start();39         }40         for (int i = 0; i < numTasks; i++)41         {42             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止43         }44         Console.WriteLine("Summarized {0}", state.State);45     }46 }

运行结果:

从上面结果看出,20个任务分别对共享的数据累加后,打印其结果。每个任务执行500次,共20个任务,理想的结果是10000,但是事实并非如此。事实是每次运行的结果都不同,且没有一个结果是正确的。使用lock语句时要注意的是传递的锁对象必须是引用对象,若对值对象使用lock语句,C#编译器会报错。

7、将上述代码改写为如下的SyncRoot模式,但是不能打印输出理想结果的10000。

1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4  5 class SharedState 6 { 7     public int State { get; set; } 8 } 9 10 class Worker11 {12     SharedState state;13 14     public Worker()15     {16         this.state = new SharedState();    17     }18 19     public Worker(SharedState state)20     {21         this.state = state;22     }23 24     public static Worker Synchronized(Worker worker)25     {26         if (!worker.IsSynchronized)27         {28             return new SynchronizedWorker(worker);29         }30         return worker;31     }32 33     public virtual void DoJob()34     {35         for (int i = 0; i < 500; i++)36         {37             state.State += 1;38         }39     }40 41     public virtual bool IsSynchronized42     {43         get { return false; }44     }45 46     private class SynchronizedWorker : Worker47     {48         object locker = new object();49         Worker worker;50 51         public SynchronizedWorker(Worker worker)52         {53             this.worker = worker;54         }55 56         public override bool IsSynchronized57         {58             get { return true; }59         }60 61         public override void DoJob()62         {63             lock (locker)64             {65                 worker.DoJob();66             }67         }68     }69 }70 71 class Program72 {73     static void Main(string[] args)74     {75         int numTasks = 20;76         var state = new SharedState();77         var tasks = new Task[numTasks];78         for (int i = 0; i < numTasks; i++)79         {80             Worker worker = Worker.Synchronized(new Worker(state));81             tasks[i] = new Task(worker.DoJob);82             tasks[i].Start();83         }84         for (int i = 0; i < numTasks; i++)85         {86             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止87         }88         Console.WriteLine("Summarized {0}", state.State);89     }90 }

将SharedState类也改写为SyncRoot模式,还是不行,不明白原因。

1 class SharedState 2 { 3     object locker = new object(); 4  5     int state; 6  7     public int State 8     { 9         get10         {11             lock (locker)12             {13                 return this.state;14             }15         }16         set17         {18             lock (locker)19             {20                 this.state = value;21             }22         }23     }24 }

最简单且可靠的办法是在DoJob方法中,将lock语句添加到合适的地方。

1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4  5 class SharedState 6 { 7     public int State { get; set; } 8 } 9 10 class Worker11 {12     SharedState state;13 14     public Worker(SharedState state)15     {16         this.state = state;17     }18 19     public void DoJob()20     {21         for (int i = 0; i < 500; i++)22         {23             // 最简单可靠的办法是在适合的地方添加lock语句24             lock (state)25             {26                 state.State += 1;27             }28         }29     }30 }31 32 class Program33 {34     static void Main(string[] args)35     {36         int numTasks = 20;37         var state = new SharedState();38         var tasks = new Task[numTasks];39         for (int i = 0; i < numTasks; i++)40         {41             tasks[i] = new Task(new Worker(state).DoJob);42             tasks[i].Start();43         }44         for (int i = 0; i < numTasks; i++)45         {46             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止47         }48         Console.WriteLine("Summarized {0}", state.State);49     }50 }

或者也可以如下重写DoJob方法。

1 public void DoJob() 2 { 3     // 最简单可靠的办法是在适合的地方添加lock语句 4     lock (state) 5     { 6         for (int i = 0; i < 500; i++) 7         { 8             state.State += 1; 9         }10     }11 }

注意:在一个地方使用lock语句并不意味着,访问对象的其他线程都正在等待。必须对每个访问共享状态的线程显示地使用同步功能。

8、Interlocked类是一个静态类型,用于使简单的语句原子化,例如,i++不是线程安全的,它的操作包括:从内存中获取一个值,给该值递增1,再将它存储回内存。所有这些操作都有可能被线程调试器打断。

1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4  5 class SharedState 6 { 7     private int state; 8     public int State 9     {10         get { return this.state; }11         set { this.state = value; }12     }13 14     public void Increment()15     {16         Interlocked.Increment(ref state); //替代this.state++;并且是线程安全的17     }18 }19 20 class Worker21 {22     SharedState state;23 24     public Worker(SharedState state)25     {26         this.state = state;27     }28 29     public void DoJob()30     {31         for (int i = 0; i < 500; i++)32         {33             state.Increment();34         }35     }36 }37 38 class Program39 {40     static void Main(string[] args)41     {42         int numTasks = 20;43         var state = new SharedState();44         var tasks = new Task[numTasks];45         for (int i = 0; i < numTasks; i++)46         {47             tasks[i] = new Task(new Worker(state).DoJob);48             tasks[i].Start();49         }50         for (int i = 0; i < numTasks; i++)51         {52             tasks[i].Wait(); //使20个任务全部处于等待状态,直到所有任务都执行完毕为止53         }54         Console.WriteLine("Summarized {0}", state.State);55     }56 }

 

转载于:https://www.cnblogs.com/luckymonkey/p/5563479.html

你可能感兴趣的文章
阿里云欧洲、中东、日本和澳洲四大区数据中心相继启用
查看>>
印度太阳能大跃进 2017年计划实现装机100GW
查看>>
MEEM数据线可以在充电时备份手机数据
查看>>
ARM与台积电签订长期战略合作协议
查看>>
连续四年走低 PC市场被压缩的根源是什么?
查看>>
大数据来了,未来还需要会计吗?
查看>>
智利扩展光纤网络 拟扩张至少2万公里
查看>>
日本经济产业省将制定法规以确保中小规模光伏设备安全
查看>>
光伏能源虚拟货币:互联网+时代的先驱者
查看>>
这个僵尸网络自2014年起已经感染了近百万台设备
查看>>
凯萨医疗机构的CIO分享数字化转型经验
查看>>
激光投影企业对会议市场发起魅力攻势
查看>>
负载压力测试中监理的工作重点
查看>>
《拥抱变化——社交网络时代的企业转型之道》一信誉和风险管理
查看>>
09_EGIT插件的安装,Eclipse中克隆(clone),commit,push,pull操作演示
查看>>
《Scala机器学习》一一2.7 总结
查看>>
《编写高质量代码:改善c程序代码的125个建议》——第2章 保持严谨的程序设计,一切从表达式开始做起 建议12:尽量减少使用除法运算与求模运算...
查看>>
nginx error_log 错误日志配置说明
查看>>
编程语言拟人化:Java、C++、Python、Ruby、PHP、C#、JS
查看>>
《BackTrack 5 Cookbook中文版——渗透测试实用技巧荟萃》—第3章3.2节服务遍历
查看>>