线程
Thread类
中的方法
start开始
json等待
前后台线程之分:前台线程结束后后台线程自动结束
static void Main(string[] args) { ThreadPool.QueueUserWorkItem(CallBackWotkItem); ThreadPool.QueueUserWorkItem(CallBackWotkItem, 1); Thread.Sleep(3000); Console.WriteLine("主线程退出"); } private static void CallBackWotkItem(object start) { Console.WriteLine($"线程开始执行{Thread.CurrentThread.ManagedThreadId}"); if (start != null) { Console.WriteLine($"线程池线程ID{Thread.CurrentThread.ManagedThreadId} 狀態{start.ToString()}"); } else { Console.WriteLine($"线程池线程ID{Thread.CurrentThread.ManagedThreadId} "); } }
线程池ThreadPool他使用线程都是后台线程,就是线程的容器,当给一个任务时候,线程池调度一个线程用来执行任务
static void Main(string[] args) { Console.WriteLine("主线程原型"); CancellationTokenSource cts = new CancellationTokenSource(); ThreadPool.QueueUserWorkItem(callback, cts.Token); Console.WriteLine("按下回車鍵取消操作"); Console.Read(); cts.Cancel(); Console.ReadKey(); } static void callback(object state) { CancellationToken token = (CancellationToken)state; Console.WriteLine("计数开始"); Count(token, 1000); } static void Count(CancellationToken token, int countto) { for (int i = 0; i < countto; i++) { if (token.IsCancellationRequested) { Console.WriteLine("计数取消"); return; } Console.WriteLine($"计数为{i}"); Thread.Sleep(300); } Console.WriteLine("计数完成"); }
使用CancellationTokenSource 下的CancellationToken 可以看到线程状态
class Program { static int tickets = 100; static void Main(string[] args) { Thread thread1 = new Thread(SaleThread1); Thread thread2 = new Thread(SaleThread2); thread1.Start(); thread2.Start(); Thread.Sleep(4000); Console.ReadKey(); /*线程1出票100 线程2出票99 线程2出票97 线程2出票96 线程2出票95 线程2出票94 线程2出票93 线程2出票92 线程2出票91 线程2出票90 线程2出票89 线程2出票88 线程2出票87 线程2出票86 线程2出票85 线程2出票84 线程2出票83 线程2出票82 线程2出票81 线程2出票80 线程2出票79 线程2出票78 线程2出票77 线程2出票76 线程2出票75
*/
/*这个地方的执行结果为什么不会产生同样的数据*/
} static void SaleThread1() { while (true) { if (tickets > 0) { Console.WriteLine("线程1出票" + tickets--); } else { break; } } } static void SaleThread2() { while (true) { if (tickets > 0) { Console.WriteLine("线程2出票" + tickets--); } else { break; } } } }
结果不是按照顺序100 99这种顺序执行的,为什么会产生这种结果?
这种结果不是我们想要的当买票的时候不能第98张还没有卖出去,97就已经卖完这不合理
class Program { static int tickets = 100; static object glpalOBj = new object(); static void Main(string[] args) { Thread thread1 = new Thread(SaleThread1); Thread thread2 = new Thread(SaleThread2); thread1.Start(); thread2.Start(); Thread.Sleep(4000); /*线程1出票100 线程2出票99 线程2出票98 线程2出票97 线程1出票96 线程1出票95 线程2出票94 线程2出票93 线程1出票92 线程1出票91 线程1出票90 线程1出票89 线程2出票88 线程2出票87 线程1出票86 线程1出票85 线程1出票84 线程2出票83 线程2出票82 线程1出票81 线程1出票80 线程2出票79 线程2出票78 线程2出票77 线程2出票76 线程1出票75*/ } static void SaleThread1() { while (true) { try { Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁 Thread.Sleep(1); if (tickets > 0) { Console.WriteLine("线程1出票" + tickets--); } else { break; } } finally { Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖 } } } static void SaleThread2() { while (true) { try { Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁 Thread.Sleep(1); if (tickets > 0) { Console.WriteLine("线程2出票" + tickets--); } else { break; } } finally { Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖 } } } }
Monitor.Enter(glpalOBj);锁定对象必须要为引用对象,不能为值对象,他加锁Enter和释放锁Exit方法必须是同一个对象,
当使用值类型的时候,会产生一个装箱的操作转换为object对象,解锁的时候也会装箱产生一个object对象,他们两个对象不一样
就会引发SynchronizationLock.Exception异常
代码分析
线程一开始运行执行Monitor.Enter方法,获得glpalOBj排他锁,然后线程一等待,线程二开始执行Monitor.Enter,但是glpalOBj
因为线程1已经加锁没有释放,所以线程2等待。知道线程1执行完成 Monitor.Exit(glpalOBj);释放完毕glpalOBj对象上的排他
锁,线程2才能执行。这样两个线程轮训执行完成了售票工作
但是现在没有解释 try finally语句。现在去除这个语句看看是什么情况
class Program { static int tickets = 100; static object glpalOBj = new object(); static void Main(string[] args) { Thread thread1 = new Thread(SaleThread1); Thread thread2 = new Thread(SaleThread2); thread1.Start(); thread2.Start(); Thread.Sleep(4000); /*线程1出票100 线程2出票99 线程2出票98 线程2出票97 线程1出票96 线程1出票95 线程2出票94 线程2出票93 线程1出票92 线程1出票91 线程1出票90 线程1出票89 线程2出票88 线程2出票87 线程1出票86 线程1出票85 线程1出票84 线程2出票83 线程2出票82 线程1出票81 线程1出票80 线程2出票79 线程2出票78 线程2出票77 线程2出票76 线程1出票75*/ } static void SaleThread1() { while (true) { Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁 Thread.Sleep(1); if (tickets > 0) { Console.WriteLine("线程1出票" + tickets--); } else { break; } Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖 } } static void SaleThread2() { while (true) { Monitor.Enter(glpalOBj);// 在obj对象上获得排他锁 Thread.Sleep(1); if (tickets > 0) { Console.WriteLine("线程2出票" + tickets--); } else { break; } Monitor.Exit(glpalOBj);// 釋放制定對象上的排他鎖 } } }
你会发现程序一直不退出,这是什么原因那,当程序开始售票知道没有票的时候
,这时候线程2开始执行 break 语句,线程1要执行Monitor.Enter(glpalOBj);语句但是因为线程二没有释放
glpalOBj对象上的排他锁,所以线程1会一致等待,线程1是前台线程所以程序不会退出,这就是我们经常说的假死状态
CPU分片
CPU在某一个时间点上确实只能执行一个线程,但是多线程不是由于多核或者双核才叫多线程。是由于,很多个线程在并行执行的时候,CPU根据一定的线程调度算法,频繁的进行线程切换,当正在zd执行的一个线程需要进行IO操作或者版需要访问内存的时候,
CPU完全可以放弃该线程,转而调度线程就绪队列上的其他线程,被放弃的线程则进入阻塞状态,IO操作或者访问内存操作结束之后,该线程权可以进入线程就绪队列上。
在程序设计的时候应该避免使用线程同步,因为他会引起一些问题
他使用比较繁琐。我们要用额外的代码把多线程同事访问的数据保卫起来,并获取和释放线程的同步锁。如果在一个代码框忘记获取锁,就可以造成数据损坏
使用线程锁同步会影响程序性能,因为获取和释放锁是需要时间的,并且决定那个线程先获取锁cpu也必须进行一个调度,这会导致线程阻塞。这些额外的工作
对性能造成一定的影响
线程同步每次只允许一个线程访问资源。这会导致线程柱塞。继而系统需要创建更多的线程,CPU也就需要负担更多的调度工作。这个工作也会给性能造成影响
static void Main(string[] args) { int x = 0; const int iterationNumber = 50000000; Stopwatch sw = new Stopwatch(); for (int i = 0; i < iterationNumber; i++) { x++; } Console.WriteLine($"不使用锁的时间{sw.ElapsedMilliseconds}"); sw.Restart(); for (int i = 0; i < iterationNumber; i++) { Interlocked.Increment(ref x); } Console.WriteLine($"使用锁的时间{sw.ElapsedMilliseconds}"); Console.ReadKey(); /*不使用锁的时间0 使用锁的时间449 */ }