我面临与pthread同步问题。 threadWaitFunction1,是一个线程等待函数。 我希望线号。 247 flag = 1
只有在243-246完成后才能执行。 但是我发现有时候,在243-246完成之前,它直接跳到了247。
请帮帮我。
提前致谢。
236 struct timespec timeToWait; 237 static void* threadWaitFunction1(void *timeToWaitPtr) 238 { 239 cout << "Setting flag =0 inside threadWaitFunction1\n"; 240 241 cout << "Inside threadWaitFunction\n"; 242 struct timespec *ptr = (struct timespec*) timeToWaitPtr; 243 pthread_mutex_lock(&timerMutex); flag = 0; 244 pthread_cond_timedwait(&timerCond, &timerMutex, ptr); flag=1; 245 pthread_mutex_unlock(&timerMutex); 246 cout << "Setting flag =1 inside threadWaitFunction1\n"; 247 248 249 }
创build并调用上述线程的线程是:
263 static void timer_trackStartTime () 264 { 265 struct timeval now; 266 pthread_t thread; 267 268 printf("Inside trackStartTime: flag = %d\n",flag); 269 270 /* Setting timer expiration */ 271 timeToWait.tv_sec = lt_leak_start_sec;; // First expiry after 1 sec 272 timeToWait.tv_nsec = lt_leak_start_nsec; 273 pthread_create(&thread, NULL, threadWaitFunction1, &timeToWait); 274 pthread_join(thread, NULL); 275 //pthread_kill(thread, SIGKILL); // Destroying the thread to ensure no leaks 276 . . 283 }
如果我使用pthread_mutex_lock保护整个函数,但仍然存在相同的问题。 如何确保有序执行? 谁能帮忙?
编辑:now.tv_sec和now.tv_nsec从代码中删除。 * 编辑:改变了互斥体内的标志(仍然不起作用) *
所以这不是真正的执行顺序(这很可能是正确的),但时机,让你不快乐。 而在“243-246之前直接跳到247”之下,你的意思是“我观察到它执行了247,在244应该等待的时间已经过去了”。 对?
然后,我怀疑这是虚假唤醒的问题:即使没有其他线程发信号通知条件变量,线程也可能被唤醒。 pthread_cond_timedwait()
的规范说:“可能会发生来自pthread_cond_timedwait()或pthread_cond_wait()函数的虚假唤醒”。
通常情况下,一个条件变量与应用程序中的某个事件相关联,而一个等待条件变量的线程实际上等待另一个线程发出感兴趣事件发生的信号。 如果你没有事件,只想等待一段时间,其他的方法,比如usleep()
或者定时器 ,更合适,除非你还需要一个pthread取消点。
ADDED:既然你对usleep()
看起来很满意,并且只问pthread_cond_timedwait()
为什么不符合你的期望,我决定不发布代码。 如果你需要的话,你可以使用@Hasturkun的答案。
ADDED-2:下面的注释中的输出(在应用Hasturkun的解决方案后获得)表明等待的线程不会退出循环,这可能意味着pthread_cond_timedwait()
返回与ETIMEDOUT不同的东西。 你有没有看到@nos对你的文章的评论(我确定了减去nanosecs的数量):
确保(now.tv_usec * 1000)+ lt_leak_start_nsec; 不溢出。 你只能设置tv_nsec为最大999999999,如果表达式大于那个,你应该从tv_nsec中减去1000000000,并且将tv_sec递增1.如果你的timeToWaitPtr包含一个无效的tv_nsec(大于999999999),pthread_cond_timedwait将会失败(你应该检查它的回报值也是。) – 4月28日19:04
在这种情况下, pthread_cond_timedwait()
将重复返回EINVAL
,并永远不会退出循环。 在进入等待循环之前,最好调整超时时间,尽管它也可以响应EINVAL
来完成。
ADDED-3:现在你改变了你的问题中的代码来通过超时而没有增加当前时间,它有另一个问题。 正如规范所述, pthread_cond_timedwait()
的超时是绝对时间,而不是相对的; 所以当你传递3秒的时间作为超时时间时,它被解释为“自系统时间的参考点以来3秒”。 那一刻几乎肯定会传递一段时间,所以pthread_cond_timedwait()
立即返回。
我建议你彻底阅读规范(包括基本原理),以便更好地理解如何使用这个函数。
保罗·E·麦肯尼(Paul E. McKenney)写了一本题为“并行编程是否困难的书”,如果是的话,你可以做些什么呢? ,这对记忆障碍有很好的信息(和一些很好的图片)。
回到你的问题, flag
不受任何保护。 虽然您可能会认为pthread_mutex_lock()
和pthread_mutex_unlock
提供了一些强有力的排序和可视性保证,但它提供的唯一保证是针对关键区域内和mutex本身的访问。
更重要的是,在某些体系结构中, pthread_mutex_lock()
使用获取屏障, pthread_mutex_unlock()
使用释放屏障,这意味着互斥体保护区域之前和之后的访问可能会溢出到互斥体保护区域。 在释放互斥锁的CPU和获取相同互斥锁的另一个CPU之间提供了强有力的排序保证,但几乎所有其他的东西都不需要(也可能没有)保证。
显然我对pthreads是错误的,他们似乎需要完全的内存障碍(如果你解释同步内存相对于其他线程的要求)。 更多关于这方面的信息以及关于Hans Boehm 在Pthread-Style Locks的Reordering Constraints约束下实际实现中提供的一些信息。
在IA64上,我还想知道NPTL 1,2 。
正如阿列克谢·库卡诺夫(Alexey Kukanov)所说,这个问题可能是虚假的。 您的代码可能会更正为循环,直到超时发生。 请注意,我也将标志设置为互斥量。
static void* threadWaitFunction1(void *timeToWaitPtr) { struct timespec *ptr = (struct timespec*) timeToWaitPtr; int ret; pthread_mutex_lock(&timerMutex); cout << "Setting flag =0 inside threadWaitFunction1\n"; flag=0; cout << "Inside threadWaitFunction\n"; while (pthread_cond_timedwait(&timerCond, &timerMutex, ptr) != ETIMEDOUT) ; cout << "Setting flag =1 inside threadWaitFunction1\n"; flag=1; pthread_mutex_unlock(&timerMutex); }
为了安全起见,你应该检查同一互斥体下的标志来建立顺序
这可能是因为编译器已经优化了一些东西,并且在线程互斥之前把你的任务放到了你的标志上。 如果要保证执行顺序(通常不能保证的事情,只要程序的可见行为不因优化而改变),就使用内存屏障来确保所需的指令按照你写的顺序执行,只能按顺序执行。
关于记忆障碍是如何工作以及他们做什么和不做什么的,这篇文章非常有趣,虽然技术性很强。 它是为Linux编写的,但基本原理保持不变。
编辑:
锁是一个隐含的内存屏障,通过我之前给出的链接,所以不需要内存屏障。
只是为了大家的信息:
我无法实现使用pthread_cond_timedwait(&timerCond, &timerMutex, ptr);
我已经实现了使用usleep( )
,usleep需要timespec
结构,我们可以使用秒和纳秒指定等待期,我的目的就解决了。
那么pthread_cond_timedwait(&timerCond, &timerMutex, ptr);
是有道理的? 我很惊讶,因为这个API有望使调用线程等待,所以满足这个条件,但似乎处理器跳转到下一条指令作为优化措施,并不等待条件满足。
但是我的问题仍然是一样的,至于为什么, pthread_cond_timedwait(&timerCond, &timerMutex, ptr);
不应该让调用线程等待?
看来我浪费了一天的时间: pthread_cond_timedwait( )