在《多线程编程》系列第一篇讲述了如何启动线程,这篇讲述线程之间存在竞争时如何确保同步并且不发生死锁。 线程不同步引出的问题 下面做一个假设,假设有100张票,由两个线程来实现一个售票程序,每次线程运行时首先检查是否还有票未售出,如果有就按照票号
在《多线程编程》系列第一篇讲述了如何启动线程,这篇讲述线程之间存在竞争时如何确保同步并且不发生死锁。
线程不同步引出的问题
下面做一个假设,假设有100张票,由两个线程来实现一个售票程序,每次线程运行时首先检查是否还有票未售出,如果有就按照票号从小到大的顺序售出票号最小的票,程序的代码如下:
这段程序的执行效果并不每次都一样,下图是某次运行效果的截图:
从上图可以看出票号为001的号被售出了两次(如果遇上像《无极》中谢霆锋饰演的那种角色,可能又会引出一场《一张票引发的血案》了,呵呵),为什么会出现这种情况呢?
请看代码③处:
ticketList.RemoveAt(0);//③
在某个情况有可能线程1恰好运行到此处,从ticketList中取出索引为0的那个元素并将票号输出,不巧的是正好分给线程1执行的时间片已用完,线程1进入休眠状态,线程2从头开始执行,它可以从容地从ticketList中取出索引为0的那个元素并且将其输出,因为线程1执行的时候虽然输出了ticketList中索引为0的那个元素但是来不及将其删除,所以这时候线程2得到的值和上次线程1得到的值一致,这就出现了有些票被售出了两次、有些票可能根本就没有售出的情况。
出现这种情况的根本原因就是多个线程都是对同一资源进行操作所致,所以在多线程编程应尽可能避免这种情况,当然有些情况下确实避免不了这种情况,这就需要对其采用一些手段来确保不会出现这种情况,这就是所谓的线程的同步。
在C#中实现线程的同步有几种方法:lock、Mutex、Monitor、Semaphore、Interlocked和ReaderWriterLock等。同步策略也可以分为同步上下文、同步代码区、手动同步几种方式。
同步上下文
同步上下文的策略主要是依靠SynchronizationAttribute类来实现。例如下面的代码就是一个实现了上下文同步的类的代码:
所有在同一个上下文域的对象共享同一个锁。这样创建的对象实例属性、方法和字段就具有线程安全性,需要注意的是类的静态字段、属性和方法是不具有线程安全性的。
同步代码区
同步代码区是另外一种策略,它是针对特定部分代码进行同步的一种方法。
lock同步
针对上面的代码,要实现不会出现混乱(两次卖出同一张票或者有些票根本就没有卖出),可以lock关键字来实现,出现问题的部分就是在于判断剩余票数是否大于0,如果大于0则从当前总票数中减去最大的一张票,因此可以对这部分进行处理,代码如下:
经过这样处理之后系统的运行结果就会正常。效果如下:
总的来说,lock语句是一种有效的、不跨越多个方法的小代码块同步的做法,也就是使用lock语句只能在某个方法的部分代码之间,不能跨越方法。
Monitor类
针对上面的代码,如果使用Monitor类来同步的话,代码则是如下效果:
当然这段代码最终运行的效果也和使用lock关键字来同步的效果一样。比较之下,大家会发现使用lock关键字来保持同步的差别不大:”lock (objLock){“被换成了”Monitor.Enter(objLock);”,”}”被换成了” Monitor.Exit(objLock);”。实际上如果你通过其它方式查看最终生成的IL代码,你会发现使用lock关键字的代码实际上是用Monitor来实现的。
如下代码:
实际上是相当于:
我们知道在绝大多数情况下finally中的代码块一定会被执行,这样确保了即使同步代码出现了异常也仍能释放同步锁。
Monitor类出了Enter()和Exit()方法之外,还有Wait()和Pulse()方法。Wait()方法是临时释放当前活得的锁,并使当前对象处于阻塞状态,Pulse()方法是通知处于等待状态的对象可以准备就绪了,它一会就会释放锁。下面我们利用这两个方法来完成一个协同的线程,一个线程负责随机产生数据,一个线程负责将生成的数据显示出来。下面是代码:
执行上面的代码在大部分情况下会看到如下所示的结果:
一般情况下会看到上面的结果,原因是t1的Start()方法在先,所以一般会优先活得执行,t1执行后首先获得对象锁,然后在循环中通过 Monitor.Wait(lockObject)方法临时释放对象锁,t1这时处于阻塞状态;这样t2获得对象锁并且得以执行,t2进入循环后通过Monitor.Pulse(lockObject)方法通知等待同一个对象锁的t1准备好,然后在生成随机数之后临时释放对象锁;接着t1获得了对象锁,执行输出t2生成的数据,之后t1通过 Monitor.Wait(lockObject)通知t2准备就绪,并在下一个循环中通过 Monitor.Wait(lockObject)方法临时释放对象锁,就这样t1和t2交替执行,得到了上面的结果。
当然在某些情况下,可能还会看到如下的结果:
至于为什么会产生这个结果,原因其实很简单,尽管t1.Start()出现在t2.Start()之前,但是并不能就认为t1一定会比t2优先执行(尽管可能在大多数情况下是),还要考虑线程调度问题,使用了多线程之后就会使代码的执行顺序变得复杂起来。在某种情况下t1和t2对锁的使用产生了冲突,形成了死锁,也就出现了如上图所示的情况,为了避免这种情况可以通过让t2延时一个合适的时间。
手控同步
手控同步是指使用不同的同步类来创建自己的同步机制。使用这种策略要求手动地为不同的域或者方法同步。
ReaderWriterLock
ReaderWriterLock支持单个写线程和多个读线程的锁。在任一特定时刻允许多个线程同时进行读操作或者一个线程进行写操作,使用ReaderWriterLock来进行读写同步比使用监视的方式(如Monitor)效率要高。
下面是一个例子,在例子中使用了两个读线程和一个写线程,代码如下:
程序的运行结果如下:
Result = 0.355650523270459.
Result = 0.125205692112756.
当然因为引入了随机数,所以每次计算结果并不相同,这里要讲述的是它们之间的控制。首先在 Result(int seed)方法中讲计算基数、第一项、第二项及第三项的方法放到线程池中,要计算第一二三项时首先要确定基数,这些方法通过manualEvent.WaitOne()暂时停止执行,于是计算基数的方法首先执行,计算出基数之后通过manualEvent.Set()方法通知计算第一二三项的方法开始,在这些方法完成计算之后通过autoEvents数组中的AutoResetEvent元素的Set()方法发出信号,标识执行完毕。这样WaitHandle.WaitAll(autoEvents)这一步可以继续执行,从而得到执行结果。
在上面代码中的WaitHandle的其它子类限于篇幅不在这里一一举例讲解,它们在使用了多少有些相似之处(毕竟是一个爹、从一个抽象类继承下来的嘛)。
图片上传功能暂时关闭,敬请谅解。

mysql'sblobissuitableForStoringBinaryDataWithInareLationalDatabase,而alenosqloptionslikemongodb,redis和calablesolutionsoluntionsoluntionsoluntionsolundortionsolunsolunsstructureddata.blobobobsimplobissimplobisslowderperformandperformanceperformancewithlararengelitiate;

toaddauserinmysql,使用:createUser'username'@'host'Indessify'password'; there'showtodoitsecurely:1)choosethehostcarecarefullytocon trolaccess.2)setResourcelimitswithoptionslikemax_queries_per_hour.3)usestrong,iniquepasswords.4)Enforcessl/tlsconnectionswith

toAvoidCommonMistakeswithStringDatatatPesInMysQl,CloseStringTypenuances,chosethirtightType,andManageEngencodingAndCollationsEttingsefectery.1)usecharforfixed lengengters lengengtings,varchar forbariaible lengength,varchariable length,andtext/blobforlabforlargerdata.2 seterters seterters seterters seterters

mysqloffersechar,varchar,text,and denumforstringdata.usecharforfixed Lengttrings,varcharerforvariable长度,文本forlarger文本,andenumforenforcingDataAntegrityWithaEtofValues。

优化MySQLBLOB请求可以通过以下策略:1.减少BLOB查询频率,使用独立请求或延迟加载;2.选择合适的BLOB类型(如TINYBLOB);3.将BLOB数据分离到单独表中;4.在应用层压缩BLOB数据;5.对BLOB元数据建立索引。这些方法结合实际应用中的监控、缓存和数据分片,可以有效提升性能。

掌握添加MySQL用户的方法对于数据库管理员和开发者至关重要,因为它确保数据库的安全性和访问控制。1)使用CREATEUSER命令创建新用户,2)通过GRANT命令分配权限,3)使用FLUSHPRIVILEGES确保权限生效,4)定期审计和清理用户账户以维护性能和安全。

chosecharforfixed-lengthdata,varcharforvariable-lengthdata,andtextforlargetextfield.1)chariseffity forconsistent-lengthdatalikecodes.2)varcharsuitsvariable-lengthdatalikenames,ballancingflexibilitibility andperformance.3)

在MySQL中处理字符串数据类型和索引的最佳实践包括:1)选择合适的字符串类型,如CHAR用于固定长度,VARCHAR用于可变长度,TEXT用于大文本;2)谨慎索引,避免过度索引,针对常用查询创建索引;3)使用前缀索引和全文索引优化长字符串搜索;4)定期监控和优化索引,保持索引小巧高效。通过这些方法,可以在读取和写入性能之间取得平衡,提升数据库效率。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

Dreamweaver CS6
视觉化网页开发工具

Atom编辑器mac版下载
最流行的的开源编辑器