C 如何确保线程被阻塞?
我有一个多线程C基准测试,可以描述如下:C 如何确保线程被阻塞?,c,multithreading,synchronization,pthreads,C,Multithreading,Synchronization,Pthreads,我有一个多线程C基准测试,可以描述如下: Thread 1 Thread 2 Thread 3 Control thread while(1) while(1) while(1) while(1) | | | | | | | | | |
Thread 1 Thread 2 Thread 3 Control thread
while(1) while(1) while(1) while(1)
| | |
| | | |
| | | every one second:
| | | wait for other threads to be blocked
| | | do something with S values
| | | |
| | | |
write S1 write S2 write S3 |
| | | |
| | | |
barrier barrier barrier barrier
我的问题涉及上图中的等待其他线程被阻塞
语句。目前,我采用以下解决方案来实现它:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <inttypes.h>
#define NB_THREADS 11
pthread_barrier_t b;
uint8_t blocked_flags[NB_THREADS] = {0};
pthread_mutex_t blocked_flags_mutexes[NB_THREADS];
uint64_t states[NB_THREADS] = {0};
uint64_t time_diff_get(struct timespec *start, struct timespec *end) {
uint64_t end_ns = end->tv_sec * 1E9 + end->tv_nsec;
uint64_t start_ns = start->tv_sec * 1E9 + start->tv_nsec;
uint64_t res = end_ns - start_ns;
return res;
}
static void *worker_thread(void *arg) {
uint8_t id = *((uint8_t *)arg);
int a = 0;
while(1) {
for (int i = 0; i < 1000; i++) {
a++;
}
states[id]++;
pthread_mutex_lock(&blocked_flags_mutexes[id]);
blocked_flags[id] = 1;
pthread_mutex_unlock(&blocked_flags_mutexes[id]);
pthread_barrier_wait(&b);
pthread_mutex_lock(&blocked_flags_mutexes[id]);
blocked_flags[id] = 0;
pthread_mutex_unlock(&blocked_flags_mutexes[id]);
}
printf ("a = %d\n", a);
return NULL;
}
static void *control_thread() {
struct timespec last_time;
clock_gettime(CLOCK_REALTIME, &last_time);
while(1) {
struct timespec time;
clock_gettime(CLOCK_REALTIME, &time);
if (time_diff_get(&last_time, &time) >= 1E9) {
// Wait for all threads to be blocked
for (int i = 0; i < NB_THREADS; i++) {
while (1) {
pthread_mutex_lock(&blocked_flags_mutexes[i]);
if (blocked_flags[i] == 1) {
pthread_mutex_unlock(&blocked_flags_mutexes[i]);
break;
}
pthread_mutex_unlock(&blocked_flags_mutexes[i]);
}
}
for (int i = 0; i < NB_THREADS; i++) {
pthread_mutex_lock(&blocked_flags_mutexes[i]);
if (blocked_flags[i] == 0) {
printf("How could I avoid to be there ??\n");
exit(-1);
}
pthread_mutex_unlock(&blocked_flags_mutexes[i]);
}
// Do some intersting stuff here with states array
// .....
// .....
// Save last time
clock_gettime(CLOCK_REALTIME, &last_time);
}
pthread_barrier_wait(&b);
}
return NULL;
}
int main() {
// Init barrier
pthread_barrier_init(&b, NULL, NB_THREADS + 1);
// Create worker threads
pthread_t threads[NB_THREADS];
uint8_t ids[NB_THREADS];
for (int i = 0; i < NB_THREADS; i++) {
ids[i] = i;
pthread_mutex_init(&blocked_flags_mutexes[i], NULL);
}
for (int i = 0; i < NB_THREADS; i++) {
pthread_attr_t attr;
pthread_attr_init(&attr);
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(i + 1, &cpu_set);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpu_set);
pthread_create(&threads[i], &attr, worker_thread, &ids[i]);
}
// Create control thread
pthread_t ctrl_thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(0, &cpu_set);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpu_set);
pthread_create(&ctrl_thread, &attr, control_thread, NULL);
// Join on worker threads
for (int i = 0; i < NB_THREADS; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
\ifndef\u GNU\u源代码
#定义GNU源
#恩迪夫
#包括
#包括
#包括
#包括
#包括
#包括
#定义NB_线程11
pthread_barrier_t b;
uint8_t blocked_标志[NB_线程]={0};
pthread_mutex_t blocked_flags_mutex[NB_THREADS];
uint64_t状态[NB_线程]={0};
uint64时间差异获取(结构时间域*开始,结构时间域*结束){
uint64\u t end\u ns=end->tv\u sec*1E9+end->tv\u nsec;
uint64\u t start\u ns=start->tv\u sec*1E9+start->tv\u nsec;
uint64\u t res=结束\u ns-开始\u ns;
返回res;
}
静态void*辅助线程(void*arg){
uint8_t id=*((uint8_t*)arg);
int a=0;
而(1){
对于(int i=0;i<1000;i++){
a++;
}
状态[id]++;
pthread_mutex_lock(&blocked_flags_mutex[id]);
阻塞的_标志[id]=1;
pthread_mutex_unlock(&blocked_flags_mutex[id]);
pthread\u barrier\u wait(&b);
pthread_mutex_lock(&blocked_flags_mutex[id]);
阻塞的_标志[id]=0;
pthread_mutex_unlock(&blocked_flags_mutex[id]);
}
printf(“a=%d\n”,a);
返回NULL;
}
静态void*控制线程(){
结构timespec最后一次;
时钟获取时间(时钟实时和上次时间);
而(1){
结构时间段时间;
时钟获取时间(时钟实时和时间);
如果(时间差获取(&上次时间和时间)>=1E9){
//等待所有线程被阻塞
对于(int i=0;i
但是在12核Intel平台上运行这个用gcc-O0
编译的基准测试清楚地表明,我在某个地方遇到了“竞争”问题,因为进程总是在几秒钟后带着消息退出。我怎样才能解决这个问题
注意:在其他问题之后,我想使用自定义屏障,但我需要继续使用pthread_屏障,而不是在互斥和cond变量上重新实现屏障。而不是为每个工作线程保留一个标志,您可以对单个计数器进行互斥保护,每个工作线程可以在即将阻塞计数器时增加该计数器,并在屏障释放计数器后减少该计数器。这将避免您等待第一个线程被阻塞,然后是第二个线程,然后是第三个线程,等等 我不知道你的控制线程在哪里退出(除了在意外的情况下),主线程似乎没有等待它 也许您还希望在工作线程之前创建控制线程
也许您还希望同步工作线程和控制线程,让它们在释放之前等待屏障并开始实际工作 我认为发生的事情可能是:
- 在control_thread()中第一次执行
,while(1)
返回一个值<1E9,因此线程直接运行到屏障中time_diff_get(&last_time,&time)
- 现在所有的工作线程最终都会遇到障碍
- 发生这种情况后,
第二次执行它的循环,并立即检查control\u thread()
blocked\u标志[i]
- 如果至少有一个线程在该线程重置其标志之前发生这种情况,那么您将具有预期的行为
很抱歉,我目前无法提供解决方案,但如果我正确理解该问题,则是解决方案的良好开端 您的代码有一个明显的争用条件。当您的线程被屏障等待解除阻塞时,它们会将标志重置为零。在他们这么做之前,他们的旗帜在一段时间内仍然是1。控制线程可以观察到这个陈旧的值1,并认为相应的线程已经准备好阻塞,而实际上该线程刚刚要清除标志,刚刚走出障碍等待:
// worker thread
pthread_barrier_wait(&b);
// No longer blocked, but blocked_flags[id] is still 1.
// At this point, the control thread grabs the mutex, and observes the 1 value
// The mistake is thinking that 1 means "I'm about to block"; it actually
// means, "I'm either about to block on the barrier, or have just finished".
pthread_mutex_lock(&blocked_flags_mutexes[id]);
blocked_flags[id] = 0;
pthread_mutex_unlock(&blocked_flags_mutexes[id]);
这种竞争条件有时足以愚弄控制线程,使其知道所有人都被阻止,从而通过其第一个循环。然后它进入第二个循环,在那里它发现n
while(1) while(1) while(1)
| | |
| | |
| | |
| | | <---- WRITE PHASE
| | |
| | |
| | |
write S1 write S2 write S3
| | |
| | |
barrier barrier barrier
| | |
| | | <--- CHECK PHASE
| | |
| | serial thread!
| | |
| | next second?-- YES -> do something with S values!
| | | NO |
| | | |
| | +------------+
| | |
barrier barrier barrier
| | |
| | |
back to top, next WRITE PHASE.