购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.5 线程同步:互斥量

线程的主要优势在于能够通过全局变量来共享信息。不过这也带来一个问题:多个线程如何安全地修改同一变量?代码清单1-12是一个线程不安全的例子:先定义了countTask函数,函数的基本功能就是完成全局变量total的100万次自增。在main函数中创建了两个线程,同时完成对total的自增计算。

代码清单1-12 线程不安全例子

程序期望的执行结果应该是200万,而实际运行的结果大相径庭,而且每次运行的结果都不一致。运行结果如下:

数据不一致的原因是多线程同时操作total+1。为避免多线程在更新共享变量时出现不一致,Linux提供了互斥量mutex。mutex确保在同一时刻仅有一个线程可以访问某共享资源,从而保证对共享资源的原子性访问。互斥量有两种状态:已锁定状态(locked)和未锁定状态(unlocked)。在任何时刻都只有一个线程可以锁定互斥量,一旦线程锁定互斥量,随即成为该互斥量的所有者,其他想获取整个信号量的线程都要进入等待状态。

1.5.1 创建互斥量

互斥量有两种创建方式:由静态变量分配与调用函数动态地创建。静态方式是POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁;动态方式是采用pthread_mutex_init函数来初始化互斥锁。创建互斥量的方法如代码清单1-13所示。

代码清单1-13 创建互斥量

mutex为互斥量对象,mutexattr用于指定互斥锁属性,如果为NULL则使用默认属性。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同类型的锁在试图对已被锁定的互斥锁加锁时的表现不同。锁有4种类型,具体如表1-11所示。

表1-11 mutex锁类型

用PTHREAD_MUTEX_INITIALIZER创建的互斥量由系统自动回收,用pthread_mutex_init函数动态创建的互斥量需要手动调用pthread_mutex_destroy(pthread_mutex_t*mutex)函数来动态回收。

初始化互斥量之后,可以调用pthread_ mutex_ lock函数来获取互斥量锁,将互斥量设置为锁定状态。pthread_mutex_lock函数定义如代码清单1-14所示。

代码清单1-14 pthread_mutex_lock函数定义

如果互斥量当前处于未锁定状态,则调用pthread_mutex_lock函数会锁定互斥量并立即返回。如果其他线程已经锁定了当前互斥量,那么pthread_mutex_ lock函数调用会一直被阻塞,直至该互斥量被其他线程解锁,才能锁定互斥量并返回。pthread_mutex_lock返回0表示锁定成功,其他的值都表示失败。

1.5.2 互斥量解锁

通过pthread_mutex_lock函数拿到互斥量的锁之后,可以进行业务逻辑处理。在做完业务逻辑处理后,需要调用pthread_mutex_unlock函数来释放互斥量的锁。如果锁释放成功则返回0,如果是其他的值都表示失败。pthread_mutex_unlock函数定义如代码清单1-15所示。

代码清单1-15 pthread_mutex_unlock函数定义

1.5.3 mutex示例

接下来对本章开头的用例稍微进行改造,改造结果如代码清单1-16所示。代码开头采用静态赋值的方式定义了mutex互斥变量。在对total值修改之前调用pthread_mutex_lock函数来获取锁,然后进行total值的修改,之后调用pthread_mutex_unlock函数来释放锁对象。

代码清单1-16 mutex示例改造结果

上述代码通过加锁、数据修改、释放锁的逻辑实现了线程同步,确保在任意时刻只有一个线程能对total变量进行修改,代码运行的结果是2000000,符合程序预期。 Q343bapPBocTuiHYvhsgKdoD8s8B/2JaWC4ttoHV7EVyCh5tcce7y/4hXy66ty2O

点击中间区域
呼出菜单
上一章
目录
下一章
×