上一篇《多线程第一次亲密接触 CreateThread与_beginthreadex本质区别》中讲到一个多线程报数功能。为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错。这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将一个表示计数的变量递增。程序在最后输出计数的值表示有今天多少个用户登录,如果这个值不等于我们启动的线程个数,那显然说明这个程序是有问题的。整个程序代码如下:
程序中模拟的是10个用户登录,程序将输出结果:
和上一篇的线程报数程序一样,程序输出的结果好象并没什么问题。下面我们增加点用户来试试,现在模拟50个用户登录,为了便于观察结果,在程序中将50个用户登录过程重复20次,代码如下:
运行结果如下图:
现在结果水落石出,明明有50个线程执行了g_nLoginCount++;操作,但结果输出是不确定的,有可能为50,但也有可能小于50。
要解决这个问题,我们就分析下g_nLoginCount++;操作。在VC6.0编译器对g_nLoginCount++;这一语句打个断点,再按F5进入调试状态,然后按下Debug工具栏的Disassembly按钮,这样就出现了汇编代码窗口。可以发现在C/C++语言中一条简单的自增语句其实是由三条汇编代码组成的,如下图所示。
讲解下这三条汇编意思:
第一条汇编将g_nLoginCount的值从内存中读取到寄存器eax中。
第二条汇编将寄存器eax中的值与1相加,计算结果仍存入寄存器eax中。
第三条汇编将寄存器eax中的值写回内存中。
这样由于线程执行的并发性,很可能线程A执行到第二句时,线程B开始执行,线程B将原来的值又写入寄存器eax中,这样线程A所主要计算的值就被线程B修改了。这样执行下来,结果是不可预知的——可能会出现50,可能小于50。
因此在多线程环境中对一个变量进行读写时,我们需要有一种方法能够保证对一个值的递增操作是原子操作——即不可打断性,一个线程在执行原子操作时,其它线程必须等待它完成之后才能开始执行该原子操作。这种涉及到硬件的操作会不会很复杂了,幸运的是,Windows系统为我们提供了一些以Interlocked开头的函数来完成这一任务(下文将这些函数称为Interlocked系列函数)。
下面列出一些常用的Interlocked系列函数:
1.增减操作
LONG__cdeclInterlockedIncrement(LONG volatile* Addend);
LONG__cdeclInterlockedDecrement(LONG volatile* Addend);
返回变量值运算后与0比较的值,等于0返回0,大于0返回正数,小于0返回负数。
LONG__cdeclInterlockedAdd(LONG volatile* Addend, LONGValue);
返回运算后的值,注意!加个负数就是减。
2.赋值操作
LONG__cdeclInterlockedExchange(LONG volatile* Target, LONGValue);
Value就是新值,函数会返回原先的值。
在本例中只要使用InterlockedIncrement()函数就可以了。将线程函数代码改成:
再次运行,可以发现结果会是唯一的。
因此,在多线程环境下,我们对变量的自增自减这些简单的语句也要慎重思考,防止多个线程导致的数据访问出错。更多介绍,请访问MSDN上Synchronization Functions这一章节,地址为 http://msdn.microsoft.com/zh-cn/library/aa909196.aspx
看到这里,相信本系列首篇《秒杀多线程第一篇 多线程笔试面试题汇总》中选择题第一题(百度笔试题)应该可以秒杀掉了吧(知其然也知其所以然),正确答案是D。另外给个附加问题,程序中是用50个线程模拟用户登录,有兴趣的同学可以试下用100个线程来模拟一下(上机试试绝对会有意外发现^_^)。
下一篇《秒杀多线程第四篇 一个经典多线程同步问题》将提出一个稍为复杂点但却非常经典的多线程同步互斥问题,这个问题会采用不同的方法来解答,从而让你充分熟练多线程同步互斥的“招式”。更多精彩,欢迎继续参阅。
转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/7429155
如果觉得本文对您有帮助,请点击‘顶’支持一下,您的支持是我写作最大的动力,谢谢。
分享到:
相关推荐
在C++编程中,多线程环境下的原子操作(Atomic Operations)是确保程序并发执行时数据一致性的重要机制。原子操作是指不可分割的操作,一旦开始执行,就不会被其他线程中断,直到该操作完成。在多线程环境中,不使用...
原子操作是不可分割的操作,其中Interlocked系列函数提供了原子操作的实现,可用来处理多线程中的简单同步问题。在多线程编程中,避免不了遇到各种经典同步问题,如生产者消费者问题、读者写者问题等。这些问题的...
7. Interlocked系列函数:由于多线程环境下的数据竞争问题,Windows提供了Interlocked系列函数来进行原子操作。原子操作保证了在多线程环境下,对变量的操作是不可分割的,确保了操作的原子性。在文章中提到的g_...
2. 全局和局部变量(Interlocked functions):原子操作,防止数据竞争。 3. 共享内存:不同线程间共享数据。 4. 套接字(Sockets):线程间的网络通信。 五、线程生命周期管理 线程的生命周期包括创建、执行、等待...
《深入分析Interlocked:理解多线程中的原子操作》 在多线程编程中,数据一致性与线程安全是至关重要的。为了确保在并发环境下数据的正确性,开发人员通常会利用各种同步机制,如锁、信号量等。其中,`Interlocked`...
`Interlocked`类提供原子操作,如`Increment`、`Decrement`,确保线程安全的计数。 4. **异步编程** .NET Framework 4.0引入了`Task Parallel Library (TPL)`,简化了异步编程。`Task`类代表一个异步操作,`async`...
在VB中,可以使用`Shared`关键字定义线程间共享的成员,或者使用`Interlocked`类进行原子操作。 3. **线程优先级**:VB允许设置线程的优先级,但过度依赖优先级可能导致优先级反转和饥饿问题,因此应谨慎使用。 4. *...
在Windows操作系统中,多线程编程是一项核心技能,特别是在开发高效、响应迅速的应用程序时。本教程将带你深入了解Windows多线程编程的基础知识,并通过实例加深理解。 首先,我们需要理解什么是线程。线程是程序...
- **Interlocked**系列函数:提供原子操作,确保在多线程环境下对共享资源的安全访问。 - **CriticalSection**:临界区对象,用于保护共享资源免受多线程并发访问的影响。 #### 四、进程与线程的概念 1. **进程**...
确保线程安全的方法包括使用`lock`、`volatile`关键字,以及使用`Interlocked`类提供的原子操作。 10. **线程异常处理** 当线程中抛出未捕获的异常时,线程会终止。使用`try/catch`块捕获异常,或者通过`...
在Windows API中,Interlocked系列函数提供了一种原子操作,用于在多线程环境中安全地更新变量。这些函数包括InterlockedIncrement、InterlockedDecrement、InterlockedExchange等,它们确保了在多线程环境下对32位...
6. **Interlocked*系列函数**:如InterlockedIncrement,InterlockedDecrement等,用于在多线程环境中进行原子操作,避免数据竞争。 7. **Thread Local Storage (TLS)**:每个线程都有自己的TLS存储区,可用于存储...
4. **线程同步**:`Monitor.Enter/Exit`用于互斥访问,`Mutex`跨进程互斥,`Semaphore`控制并发数量,`Interlocked`类进行原子操作,防止数据竞争。 5. **异步编程**:`async/await`关键字使得异步编程更加简洁,`...
- **线程安全的函数**:如Win32的Interlocked系列函数,提供原子操作,避免竞态条件。 8. **线程生命周期管理** - **ExitThread函数**:线程主动结束自己的运行。 - **WaitForSingleObject/...
C#提供了多种线程安全的数据结构,如ConcurrentQueue、ConcurrentStack等,以及线程安全的静态类如Interlocked,用于原子操作。 总结,C#的多线程编程是一项重要技能,能够显著提高程序性能和用户体验。通过理解和...
- **Interlocked类**:提供了一组原子操作,用于在多线程环境下安全地修改变量值。 - **Task Parallel Library(TPL)**:提供了基于任务的并行编程模型,简化了多线程编程。 总之,C#作为一种现代化的编程语言,在...
`Interlocked`类在.NET框架中提供了一组原子操作方法,这些操作可以保证在多线程环境下的线程安全。原子操作是指在进行该操作时,不会被其他线程中断,从而避免了多线程竞争条件的问题。 在给定的内容中,`...
在C#编程中,多线程技术是实现并发执行任务的关键。本文将对C#多线程进行详细总结,包括如何创建线程、同步问题以及使用不同的同步机制。 1. 创建线程 1.1 异步委托方式 异步委托是通过.NET线程池创建线程的方法...
必要时使用`lock`关键字或`Interlocked`类进行原子操作。 通过这个练习,你将有机会深入了解多线程环境下资源传递的细节,掌握如何利用C#的委托机制来协调线程间的操作,以及如何通过同步机制来防止竞态条件和死锁...