LINUX环境下多线程编程肯定会遇到需要条件变量的情况,此时必然要使用pthread_cond_wait()和其他发信号的函数函数,然而在了解这部内容的过程中,有些疑惑。
一、代码
下面是笔者在学习APUE的过程中使用多线程相关知识实现对指定范围内的质数的筛选,并在屏幕上打印出找到的质数。
1 |
|
二、疑惑点: pthread_cond_broadcast与pthread_mutex_unlock的先后顺序问题
chatGPT给出的答案:
在多线程编程中,pthread_mutex_unlock
和 pthread_cond_broadcast
的执行顺序通常是没有明确要求的,因为它们操作的是不同的同步原语:互斥锁和条件变量。
网上大多数的代码给出的答案:
先pthread_cond_broadcast再pthread_mutex_unlock
笔者个人的思考总结:
- 先pthread_cond_broadcast再pthread_mutex_unlock会出现的问题:这样做pthread_cond_broadcast处在临界区内,只有当pthread_mutex_unlock解锁之后,其他子线程才有机会加锁,加锁成功的线程才能cond_wait收到广播信号,倘若在pthread_cond_broadcast与pthread_mutex_unlock之间还有很多代码需要执行,那此刻pthread_cond_broadcast尽管发送了信号,但其也是阻塞状态,只有当unlock之后其他子线程才能抢锁然后cond_wait收到cond_broadcast发送的信号。
- 先pthread_mutex_unlock 再pthread_cond_broadcast会出现的问题:通知时机问题: ( 如果先解锁再广播,有可能在解锁后,条件变量的等待线程还未来得及执行。这样,等待线程可能错过了通知,从而导致它们继续等待而不被唤醒。从而使程序出现不期望的结果) 竞态条件: (如果在解锁互斥锁后,调用
pthread_cond_broadcast
之前有其他线程抢占执行,并尝试获取互斥锁进行修改,那么这些修改可能与pthread_cond_broadcast
同时发生,导致不一致的状态。意思就是,若存在刚解锁还没来得及broadcast时,当前线程被操作系统调度出CPU,而其他线程刚好被调度并尝试获取相同的互斥锁,此时线程就有机会在广播之前获取互斥锁并修改共享资源。接着,执行广播的线程再次获取互斥锁,执行广播,但但有线程已经修改了共享资源,这可能导致不一致的状态,产生竞态条件。)
总结:
先解锁再广播,虽然等待条件的线程能够尽早获得互斥锁然后cond_wait可以很快的收到广播信号,但是这种做法存在潜在的问题,可能会导致在广播发送信号的瞬间其他线程修改了共享资源,从而可能引起竞态条件或不一致性。而先再临界区内广播然后再解锁,这样可以避免以上潜在问题,代价就是速度变慢了,若在调用 pthread_cond_broadcast 之前,互斥锁已经被解锁。这样一来,虽然被唤醒的线程就可以尽快获得互斥锁,进入临界区执行,但是,若在调用pthread_cond_broadcast广播的时候其他线程瞬间修改共享资源,会出现错误。
尽管先unlock 再broadcast,出现错误的概率可能很低,比中彩票的概率还低,但从逻辑分析,这种问题是存在的,所以为了避免这种不期望事情发生,一般采取先broadcast 然后再unlock。
三、one more thing(求质数的巧妙方法)
这里判断质数的方法是
1 | mark = 1; |
其中i是待判断的数。
质数的定义:只能分解为1和它本身两个因数
上面的做法给i/2是为了减少计算量,直接写i也没问题,但是为什么可以写i/2呢?
这是因为如果 i
有大于i/2
的因子,那么它的另一个因子一定小于i/2
,否则两个因子的乘积就会超过 i
。因此,只需要判断 i
是否在2
到i/2
的范围内有因子即可。
还有一种做法是,要判断到sqrt(i)
即可,因为如果 i
有一个大于sqrt(i)
的因子,那么它的另一个因子一定小于sqrt(i)
,两个因子的乘积就会超过 i
。因此,循环的条件可以改为 j <= sqrt(i)
。这样也可以减少不必要的循环次数,但由于要调用sqrt对应的函数库或者自己写相关的开平方函数,所以没有采用这种方法。
- 本文标题:关于APUE中多线程编程的条件变量相关内容
- 创建时间:2023-12-10 10:01:56
- 本文链接:2023/12/10/关于APUE中多线程编程的条件变量相关内容/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!