1、如果两个或多个线程访问相同的对象,或者访问不同步的共享状态,会出现争用条件。
1 using System; 2 using System.Threading; 3 4 class Outputer 5 { 6 public void Output(string msg) 7 { 8 for (int i = 0; i < msg.Length; i++) 9 { 10 Console.Write(msg[i]); 11 } 12 Console.WriteLine(); 13 } 14 } 15 16 class Program 17 { 18 static void Main(string[] args) 19 { 20 Outputer outputer = new Outputer(); 21 object locker = new object(); 22 new Thread((msg) => 23 { 24 while (true) 25 { 26 outputer.Output(msg.ToString()); 27 } 28 }).Start("abcdef"); 29 new Thread(() => 30 { 31 while (true) 32 { 33 outputer.Output("1234567890"); 34 } 35 }).Start(); 36 } 37 }
运行结果:
2、要避免该问题,可以使用lock语句锁定共享的对象。
1 using System; 2 using System.Threading; 3 4 class Outputer 5 { 6 public void Output(string msg) 7 { 8 for (int i = 0; i < msg.Length; i++) 9 { 10 Console.Write(msg[i]); 11 } 12 Console.WriteLine(); 13 } 14 } 15 16 class Program 17 { 18 static void Main(string[] args) 19 { 20 Outputer outputer = new Outputer(); 21 object locker = new object(); 22 new Thread((msg) => 23 { 24 while (true) 25 { 26 lock (locker) 27 { 28 outputer.Output(msg.ToString()); 29 } 30 } 31 }).Start("abcdef"); 32 new Thread(() => 33 { 34 while (true) 35 { 36 lock (locker) 37 { 38 outputer.Output("1234567890"); 39 } 40 } 41 }).Start(); 42 } 43 }
运行结果:
3、也可以将共享对象设置为线程安全的对象。
1 using System; 2 using System.Threading; 3 4 class Outputer 5 { 6 object locker = new object(); 7 8 public void Output(string msg) 9 { 10 lock (locker) 11 { 12 for (int i = 0; i < msg.Length; i++) 13 { 14 Console.Write(msg[i]); 15 } 16 Console.WriteLine(); 17 } 18 } 19 } 20 21 class Program 22 { 23 static void Main(string[] args) 24 { 25 Outputer outputer = new Outputer(); 26 new Thread((msg) => 27 { 28 while (true) 29 { 30 31 outputer.Output(msg.ToString()); 32 } 33 }).Start("abcdef"); 34 new Thread(() => 35 { 36 while (true) 37 { 38 39 outputer.Output("1234567890"); 40 } 41 }).Start(); 42 } 43 }
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 Program 39 { 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 Worker 11 { 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 Program 29 { 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,但是事实并非如此。
时间: 2024-11-09 00:59:25