Menu
我公司是结合网络技术为废品行业服务最早,回收技术最专业的废品回收公司。公司设立在辽宁沈阳地区,从事20多年回收行业,值得信赖!

当前位置主页 > 锁具 >

C言语线程互斥和原子操作

日期:2019-11-28 13:52 来源: 锁具

  如果多个线程访问相同的数据,并且它们中至少有一个修改了数据,那么对共享数据的所有访问必须同步以防止数据竞争。但是,

  在程序结束时,计数器应为 0。然而,在没有同步的情况下,结果则不同:程序每次运行时,最终获得计数器值都是不同的。下面是一个典型的输出示例:Counter: -714573 CPU time: 59 ms

  互斥互相排斥(mutex exclusion)技术,简称为互斥(mutex),它用于防止多个线程同时访问共享资源。互斥技术采用一个对象控制独占访问权限,该对象称之为互斥。配合条件变量(condition variable),互斥可以实现广泛的同步访问控制。例如,它们允许程序员为数据访问操作指定执行次序。在 C 程序中,一个互斥采用类型为 mtx_t 的对象表示,它能在一段时间内被一个线程锁定,而其他线程必须等待,直到它被解锁。在头文件 threads.h 中,包括了关于互斥操作的所有声明。

  mtx_plain 表示请求一个简单的互斥,它既不支持超时也不支持递归,而其他 3 个值则表示支持超时和(或)递归。

  阻塞正在调用的线程,直到该线程获得参数 mtx 引用的互斥。除该互斥支持递归的情况以外,正在调用的线程不能是已持有该互斥的线程。如果调用成功获得互斥,则函数返回值 thrd_success,否则,返回值 thrd_error。

  释放参数 mtx 引用的互斥。在调用函数 mtx_unlock()之前,调用者必须持有该互斥。如果调用释放互斥成功,则函数返回值 thrd_success,否则,返回值 thrd_error。

  通常情况下,在代码某个关键区间(critical section)的起始点调用函数 mtx_lock(),在其结束点调用函数 mtx_unlock(),在这段区间中只有一个线程执行。

  函数 mtx_lock()还有两个替代的选择:一个选择是函数 mtx_trylock()

  ,如果该互斥恰好未被其他任何线程获取,它则为当前线程获得互斥,如果该互斥被其他线程获取,它也不会阻塞当前线程;

  另一个选择是函数 mtx_timedlock(),它仅在指定的时间内阻塞线程。所有这些函数都通过其返回值表明调用它们后,是否成功地获得了互斥。例 2 中的程序是例 1 的修改版本,它展示了如何使用互斥来消除对变量 counter 的数据竞争。

  函数 incFunc()和 decFunc()将不再并行地访问 counter,因为一次只有其中一个可以锁定互斥(为保障可读性,省略错误检查)。现在,在程序结束时,计数器具有正确的值:0。下面是一个典型的输出示例:Counter: 0 CPU time: 650 ms

  实现同步性需要付出代价。较高的 CPU 时间表明:修改后的程序需要大约 10 倍于原来的时间来运行。其原因是,通过锁定互斥实现同步性远比自增和自减一个变量具有更为复杂的操作。在不需要互斥锁定的情况下,使用原子对象可以获得更好的性能。原子对象

  原子对象(atomic object)是一个可通过原子操作(atomic operation)被读取或修改的对象。原子操作是指不能被并行线标准下,可以使用类型限定符_Atomic声明一个原子对象(如果实现版本定义了宏__STDC_NO_ATOMICS__,则表明该实现版本不支持原子操作,自然也不能声明原子对象)。例如,在例14-2程序中的变量counter可以通过以下方式声明它为原子对象:_Atomic long counter = ATOMIC_VAR_INIT(0L);上述声明定义了原子化的 long 类型变量 counter,并将其值初始化为 0。在头文件 stdatomic.h 中定义了宏 ATOMIC_VAR_INIT,以及其他所有用于原子对象的宏、类型和声明。特别是,stdatomic.h 中还定义了对应于所有整数类型的原子类型缩写。例如,类型 atomic_uchar 等效于 _Atomic unsigned char。语法 _Atomic(T)也可用于为给定的非原子类型 T 指定其对应的原子类型。数组和函数类型不能为原子类型。

  读取或写入一个原子对象是一个原子操作,也就是说它是不能被中断的操作。这意味着:不同的线程可以同时访问一个原子对象而不引起竞态条件。对于每个原子对象,对象的所有修改以一个确定的全局化次序执行,这称为该对象的修改次序(modification order)。具有结构或联合类型的原子对象只能被作为一个整体读取或写入:为了安全地访问单个成员,原子结构或联合应首先复制到等效的非原子对象中。注意,无论是使用宏 ATOMIC_VAR_INIT,还是通过泛型函数 ATOMIC_INIT(),

  原子操作通常用于进行读-修改-写操作。例如,后缀自增和自减运算符 ++ 和 --,当它们应用于原子对象时,是原子化的读-修改-写操作。同样,复合赋值运算符,如 +=,当其原子化使用时,它们的左操作数是一个原子对象。

  例 1 中的程序可以通过声明变量 counter 作为原子对象,在不受任何其他影响下执行正确的计数,以最终获得 0 值。该方案计时结果显示,使用原子类型变量 counter 比例 2 所使用的互斥方法要快两倍多。除了已经提到的运算符,还有

  原子类型具有无锁(lock-free)属性,它表示不使用锁定和解锁操作实现对一个原子对象的原子访问。该方式只需要使用类型 atomic_flag(它是一个结构类型)以确保实现无锁,atomic_flag 有“设置”和“清除”两种状态。宏 ATOMIC_FLAG_INIT 将一个 atomic_flag 对象初始化为“清除”状态,如以下示例声明所示:

  在函数参数声明中的占位符A代表任一原子类型。因此,参数 obj 为指针,它指向任一给定原子对象。

  通俗易懂,深入浅出,一篇文章只讲一个知识点。文章不深奥,不需要钻研,在公交、在地铁、在厕所都可以阅读,随时随地涨姿势。

锁具

上一篇:

下一篇:没有了