求解pthread_cond_timedwait死锁的问题

sl51314240 2014-11-08 04:49:04
以下是在Linux模拟Windows Event的代码,当参数vMillisecond设为0的时候,空跑几个小时,在干净环境下必然在pthread_cond_timewait死锁不动,而且必须跑几个小时1~9不定,必现,求解,代码如下


int MC_KKEvent::WaitFinite(UINT32 vMillisecond)
{
pthread_mutex_lock(&m_Mutex);

/*
timespec.tv_sec //秒
timespec.tv_nsec //纳秒

timeval.tv_sec //秒
timeval.tv_usec //微妙
*/

struct timespec tAbstime;

struct timeval tTimeNow;

gettimeofday(&tTimeNow, 0);

tAbstime.tv_sec = tTimeNow.tv_sec + vMillisecond/1000;

tAbstime.tv_nsec = tTimeNow.tv_usec*1000 + (vMillisecond%1000)*1000000;
//纳米变为秒
const long BILLION = 1000000000;

if(BILLION <= tAbstime.tv_nsec)

{

tAbstime.tv_nsec -= BILLION;

tAbstime.tv_sec++;

}


int tRetVal = KKEVENT_SIGNALED;
while(false == m_State)
{
//收到外部中断退出
if(true == m_Break)
{
tRetVal = KKEVENT_FAILED;
break;
}

int tResult = pthread_cond_timedwait(&m_Cond, &m_Mutex, &tAbstime);
if((0 != tResult)&&(ETIMEDOUT != tResult))
{
tRetVal = KKEVENT_FAILED;
break;
}

//超时
if(ETIMEDOUT==tResult)
{
tRetVal = KKEVENT_TIMEOUT;
break;
}
}


//收到外部中断退出
if(true == m_Break)
tRetVal = KKEVENT_FAILED;

//若为自动置为事件,则置状态为false
if(false == m_ManualReset)
m_State = false;

pthread_mutex_unlock(&m_Mutex);
return tRetVal;
}
...全文
1911 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
looklzg1108 2015-11-19
  • 打赏
  • 举报
回复
pthread_cond_timedwait 这个函数在centos上理论上是有BUG的。

同样的代码,执行这个函数等待60秒的时候,在suse 和 centos上分别修改时间,比如都把当前系统时间修改为提前十分钟,则suse上只需要等待60秒即可解锁,而在centos上则需要10*60 + 60 秒才可以解锁。

目前只分析道这一步,但是其他的暂时还没定位到。
其他大神有建议吗?
sl51314240 2014-11-12
  • 打赏
  • 举报
回复
我有做了一个测试,在pthread_cond_timedwait的过程中,给该进程发Linux系统信号,进程捕捉该信号,结果是,pthread_cond_timedwait根本就不鸟信号,必须用条件变量唤醒或者超时才会返回,又一次无解…… 有谁遇到和我一样的问题吗???help!!!
sl51314240 2014-11-12
  • 打赏
  • 举报
回复
引用 11 楼 china_xiaocainiao 的回复:
[quote=引用 9 楼 sl51314240 的回复:] 查了下资料,抓狂了……就算这样,也不应该有问题啊,两次调用pthread_mutex_unlock,只是解锁失败嘛,我只有一个线程执行wait(0),所以pthread_cond_timedwait是单线程运行的,我实在不明白哪里会造成死锁……
你初始化mutex时属性值用的是0,即DEFAULT模式;我查看了一下pthread_mutex_lock的man手册,貌似DEFAULT模式下多次解锁会造成不确定行为。[/quote] 我单独试了一下,就算是故意搞错,也没卡住……
	pthread_mutex_t tMutex;
	pthread_mutex_init(&tMutex, 0);
	pthread_mutex_unlock(&tMutex);	//这里故意搞破坏

	pthread_cond_t tCond;
	pthread_cond_init(&tCond, 0);

	struct timespec tAbstime;

    struct timeval 	tTimeNow;

    gettimeofday(&tTimeNow, 0);

    tAbstime.tv_sec  = tTimeNow.tv_sec;

    tAbstime.tv_nsec = tTimeNow.tv_usec*1000;

	//这里仍然立刻返回,错误吗110,ETIMEOUT
	int tRetVal = pthread_cond
sl51314240 2014-11-12
  • 打赏
  • 举报
回复
引用 10 楼 lsyyff23 的回复:
tAbstime.tv_sec = tTimeNow.tv_sec + vMillisecond/1000; //这里的时间错了吧,既然传进来纳秒,/1000 只能是us ,
参数是毫秒
中国小菜鸟 2014-11-12
  • 打赏
  • 举报
回复
引用 9 楼 sl51314240 的回复:
查了下资料,抓狂了……就算这样,也不应该有问题啊,两次调用pthread_mutex_unlock,只是解锁失败嘛,我只有一个线程执行wait(0),所以pthread_cond_timedwait是单线程运行的,我实在不明白哪里会造成死锁……
你初始化mutex时属性值用的是0,即DEFAULT模式;我查看了一下pthread_mutex_lock的man手册,貌似DEFAULT模式下多次解锁会造成不确定行为。
lovzm 2014-11-12
  • 打赏
  • 举报
回复
tAbstime.tv_sec = tTimeNow.tv_sec + vMillisecond/1000; //这里的时间错了吧,既然传进来纳秒,/1000 只能是us ,
sl51314240 2014-11-12
  • 打赏
  • 举报
回复
引用 17 楼 china_xiaocainiao 的回复:
可以在wait中pthread_mutex_lock时判断一下返回值,看是否成功
调试代码中有加,所有pthread_mutex_lock、pthread_mutex_unlock都没有返回错误……我现在怀疑可能不是这里的问题,而是其他地方内存错误了,只是体现在这里……
中国小菜鸟 2014-11-12
  • 打赏
  • 举报
回复
引用 5 楼 sl51314240 的回复:
唉,直接贴算了,求帮忙分析 调用时使用new MC_KKEvent(true, true)创建手动模式的事件,然后主线程偶尔调用Signaled与Nonsignaled,另有一个线程在一直调用Wait(0),调试可以发现时间等参数是正确的,从__owner看到,可能是锁被抢了,我已知pthread_cond_timedwait调用时,若没有拿到锁,将在pthread_cond_timewait死等,但是我确实没有从我的代码中发现什么情况下可能导致这样的情况。 确定只调用了new MC_KKEvent()、Signaled()、Nonsignaled()、Wait(0)而没有调用其他函数 系统是CentOS 6.4 64位
可以在wait中pthread_mutex_lock时判断一下返回值,看是否成功
sl51314240 2014-11-12
  • 打赏
  • 举报
回复
B线程,是一个基于pthread的Timer的实现,wait(0),其实是不等待,把Timer当普通线程使用,0是间隔时间为0 for(;;) { //业务代码 ... wait(m_IntervalMS); } A线程,主线程,控制Timer的启停 初始化调用nosignaled(),让wait等待指定间隔后执行下一个循环; 反初始化调用signaled(),以便业务控制能快速退出;
中国小菜鸟 2014-11-12
  • 打赏
  • 举报
回复
引用 14 楼 sl51314240 的回复:
我有做了一个测试,在pthread_cond_timedwait的过程中,给该进程发Linux系统信号,进程捕捉该信号,结果是,pthread_cond_timedwait根本就不鸟信号,必须用条件变量唤醒或者超时才会返回,又一次无解…… 有谁遇到和我一样的问题吗???help!!!
不知道wait()、signaled()、nonsignaled()之间可能的调用顺序和调用次数是多少? 从目前的代码看,运行时可能会丢失一些signal
sl51314240 2014-11-11
  • 打赏
  • 举报
回复
查了下资料,抓狂了……就算这样,也不应该有问题啊,两次调用pthread_mutex_unlock,只是解锁失败嘛,我只有一个线程执行wait(0),所以pthread_cond_timedwait是单线程运行的,我实在不明白哪里会造成死锁……
sl51314240 2014-11-11
  • 打赏
  • 举报
回复
不过经过您的提醒,我注意到另一点,假如pthread_cond_timedwait被Linux信号打断,那此时锁是解锁状态还是未解锁状态??如果是未解锁,那我的代码应该没有问题,但是如果是解锁状态,那我的代码等于连续两次调用pthread_mutex_unlock,那卡死在那里就有可能了。 我明天单独做个用信号打断的测试,如果是的话,下面的代码就有解释了 int tResult = pthread_cond_timedwait(&m_Cond, &m_Mutex, &tAbstime); if((0 != tResult)&&(ETIMEDOUT != tResult)) { tRetVal = KKEVENT_FAILED; break; } 这里要判断EINTR,如果被中断,这里如果已经是解决,那就不能继续向下执行解锁操作。 我只希望被打断不要造成两种结果,可能解锁,也可能没解锁,那样我只能骂Linux的系统开发者了,无解,囧
sl51314240 2014-11-11
  • 打赏
  • 举报
回复 1
多谢,我看了一下,应该没问题,这样写只会造成虚假的唤醒。 1.B线程拿锁 2.B线程pthread_cond_timedwait,该函数会自动造成解锁, 3.A线程拿锁 4.A线程触发信号 5.B线程唤醒,并尝试拿锁,造成B线程阻塞 6.A线程解锁 7.B线程抢到锁,并向下执行 Wait的while循环,已经考虑到多个线程调用wait,仅一个线程能抢到锁的情况,因此这段代码逻辑暂未发现错误。 您推荐的那本书最后也说了,Posix明确允许这么做:调用pthread_cond_signal的线程不必是与之关联的互斥锁的当前属主。不过Posix接着说:如果需要可预见的调度行为,那么调用pthread_cond_signal的线程必须锁住该互斥锁。
zhxianbin 2014-11-11
  • 打赏
  • 举报
回复
l楼主看一下 《UNIX 网络编程》卷 2:进程间通信 的 7.5 条件变量:等待与信号发送。
pthread_mutex_lock(&m_Mutex);
m_State = true;
  if(true == m_ManualReset)
    tResult = (0 == pthread_cond_broadcast(&m_Cond));
  else
    tResult = (0 == pthread_cond_signal(&m_Cond));
 
  pthread_mutex_unlock(&m_Mutex);
改为
pthread_mutex_lock(&m_Mutex);
m_State = true;
pthread_mutex_unlock(&m_Mutex);

if(true == m_ManualReset)
    tResult = (0 == pthread_cond_broadcast(&m_Cond));
else
    tResult = (0 == pthread_cond_signal(&m_Cond));
试试
sl51314240 2014-11-11
  • 打赏
  • 举报
回复
唉,直接贴算了,求帮忙分析 调用时使用new MC_KKEvent(true, true)创建手动模式的事件,然后主线程偶尔调用Signaled与Nonsignaled,另有一个线程在一直调用Wait(0),调试可以发现时间等参数是正确的,从__owner看到,可能是锁被抢了,我已知pthread_cond_timedwait调用时,若没有拿到锁,将在pthread_cond_timewait死等,但是我确实没有从我的代码中发现什么情况下可能导致这样的情况。 确定只调用了new MC_KKEvent()、Signaled()、Nonsignaled()、Wait(0)而没有调用其他函数 系统是CentOS 6.4 64位
//---------------------------------------------------------------------------
#ifndef MC_KKEventH
#define MC_KKEventH
//---------------------------------------------------------------------------
#include "ZAVXShareIncLib.h"
//---------------------------------------------------------------------------
#define KKEVENT_SIGNALED 	 0
#define	KKEVENT_FAILED		-1
#define KKEVENT_TIMEOUT 	-2
//---------------------------------------------------------------------------
#ifdef WIN32
	#define KKEVENT_INFINITE INFINITE
#else
	#define KKEVENT_INFINITE (UINT32)-1
#endif
//---------------------------------------------------------------------------
class MC_KKEvent
{
private:
#ifdef WIN32
	HANDLE		m_HandleWait[2];
#else
	int		WaitInfinite	(void);
	int		WaitFinite	(UINT32 vMillisecond);

	pthread_mutex_t	m_Mutex;
	pthread_cond_t	m_Cond;
	bool			m_State;	//linux实现自动置位的标志
	bool			m_Break;	//外部调用Close的停止标志
#endif
	bool 			m_ManualReset;

public:
	MC_KKEvent(bool vState=false, bool vManualReset=false);
	~MC_KKEvent(void);

	int		Wait		(UINT32 vMillisecond=KKEVENT_INFINITE);
	bool	Signaled 	(void);
	bool	Nonsignaled (void);

	bool	Close		(void);	//调用此函数,将导致之前所有的Wait会返回KKEVENT_FAILED, 之后本身将不可再使用.

	bool	m_Available;
};
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#include "MC_KKEvent.h"
#include "MF_KKKernelFunc.h"
#ifndef WIN32
	#include <sys/time.h>
	#include <errno.h>
#endif
//---------------------------------------------------------------------------`
MC_KKEvent::MC_KKEvent(bool vState, bool vManualReset)
{
	m_Available = false;

#ifdef WIN32
	m_HandleWait[0] = CreateEvent(0, vManualReset, vState, 0);
	if(0 == m_HandleWait[0])
		return;

	m_HandleWait[1] = CreateEvent(0, true, false, 0);
	if(0 == m_HandleWait[1])
		return;

	m_ManualReset = vManualReset;
#else
	if(0 != pthread_mutex_init(&m_Mutex, 0))
		return;

	if(0 != pthread_cond_init(&m_Cond, 0))
		{
		pthread_mutex_destroy(&m_Mutex);
		return;
		}

	m_State			= vState;
	m_ManualReset	= vManualReset;
	m_Break			= false;
#endif

	m_Available = true;
}
//---------------------------------------------------------------------------
MC_KKEvent::~MC_KKEvent(void)
{
	if(false == m_Available)
		return;

#ifdef WIN32
	avx_SafeCloseH(m_HandleWait[0]);
	avx_SafeCloseH(m_HandleWait[1]);
#else
	pthread_mutex_destroy(&m_Mutex);
	pthread_cond_destroy(&m_Cond);
#endif

	m_Available = false;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef WIN32
int	MC_KKEvent::WaitInfinite(void)
{
	pthread_mutex_lock(&m_Mutex);

	int tRetVal = KKEVENT_SIGNALED;
	while(false == m_State)
		{
		//收到外部中断退出
		if(true == m_Break)
			{
			tRetVal = KKEVENT_FAILED;
			break;
			}

		if(0 != pthread_cond_wait(&m_Cond, &m_Mutex))
			{
			tRetVal = KKEVENT_FAILED;
			break;
			}
		}

	//收到外部中断退出
	if(true == m_Break)
		tRetVal = KKEVENT_FAILED;

	//若为自动置为事件,则置状态为false
	if(false == m_ManualReset)
		m_State = false;

	pthread_mutex_unlock(&m_Mutex);
	return tRetVal;
}
//---------------------------------------------------------------------------
int	MC_KKEvent::WaitFinite(UINT32 vMillisecond)
{
	pthread_mutex_lock(&m_Mutex);

	/*
	timespec.tv_sec		//秒
	timespec.tv_nsec    //纳秒

	timeval.tv_sec		//秒
	timeval.tv_usec		//微妙
	*/

    struct timespec tAbstime;
    struct timeval 	tTimeNow;
    gettimeofday(&tTimeNow, 0);
    tAbstime.tv_sec  = tTimeNow.tv_sec + vMillisecond/1000;
    tAbstime.tv_nsec = tTimeNow.tv_usec*1000 + (vMillisecond%1000)*1000000;
	//纳米变为秒
	const long BILLION = 1000000000;
    if(BILLION <= tAbstime.tv_nsec)
		{
        tAbstime.tv_nsec -= BILLION;
        tAbstime.tv_sec++;
		}

	int tRetVal = KKEVENT_SIGNALED;
	while(false == m_State)
		{
		//收到外部中断退出
		if(true == m_Break)
			{
			tRetVal = KKEVENT_FAILED;
			break;
			}

		int tResult = pthread_cond_timedwait(&m_Cond, &m_Mutex, &tAbstime);
		if((0 != tResult)&&(ETIMEDOUT != tResult))
			{
			tRetVal = KKEVENT_FAILED;
			break;
			}

		//超时
		if(ETIMEDOUT==tResult)
			{
			tRetVal = KKEVENT_TIMEOUT;
			break;
			}
		}

	//收到外部中断退出
	if(true == m_Break)
		tRetVal = KKEVENT_FAILED;

	//若为自动置为事件,则置状态为false
	if(false == m_ManualReset)
		m_State = false;

	pthread_mutex_unlock(&m_Mutex);
	return tRetVal;
}
#endif
//---------------------------------------------------------------------------
int MC_KKEvent::Wait(UINT32 vMillisecond)
{
	if(false == m_Available)
		return KKEVENT_FAILED;

#ifdef WIN32
	UINT32 tWaitRet = WaitForMultipleObjects(2, m_HandleWait, false, vMillisecond);
	switch(tWaitRet)
		{
		case WAIT_TIMEOUT:	return KKEVENT_TIMEOUT;
		case WAIT_OBJECT_0:	return KKEVENT_SIGNALED;
		default:			return KKEVENT_FAILED;
		}
#else
	if((UINT32)-1 == vMillisecond)
		return WaitInfinite();
	else
		return WaitFinite(vMillisecond);
#endif
}
//---------------------------------------------------------------------------
bool MC_KKEvent::Signaled(void)
{
	if(false == m_Available)
		return false;

	bool tResult = false;

#ifdef WIN32
	if(false != SetEvent(m_HandleWait[0]))
		tResult = true;
#else
	pthread_mutex_lock(&m_Mutex);

	m_State = true;
	if(true == m_ManualReset)
		tResult = (0 == pthread_cond_broadcast(&m_Cond));
	else
		tResult = (0 == pthread_cond_signal(&m_Cond));

	pthread_mutex_unlock(&m_Mutex);
#endif

    return tResult;
}
//---------------------------------------------------------------------------
bool MC_KKEvent::Nonsignaled(void)
{
	if(false == m_Available)
		return false;

#ifdef WIN32
	if(false != ResetEvent(m_HandleWait[0]))
		return true;

	return false;
#else
	pthread_mutex_lock(&m_Mutex);
	m_State = false;
	pthread_mutex_unlock(&m_Mutex);

    return true;
#endif
}
//---------------------------------------------------------------------------
bool MC_KKEvent::Close(void)
{
	if(false == m_Available)
		return false;

#ifdef WIN32
	if(false == SetEvent(m_HandleWait[1]))
		return false;
#else
	m_Break = true;
	pthread_cond_broadcast(&m_Cond);
#endif
	this->~MC_KKEvent();
	return true;
}
//---------------------------------------------------------------------------
sl51314240 2014-11-11
  • 打赏
  • 举报
回复
时间这里没有问题,只是调试m_Mutex,这个pthread_mutex_t中的__owner字段为0,比较奇怪,一般情况下,拿到锁之后,锁,__owner为拿到锁的线程号,跟syscall(SYS_gettid)得到的值一样,但是卡死的时候为0,锁丢失了,但是我确实不知道为什么会造成这样,如果需要,我可以贴出完整类实现的代码,求帮忙分析
//---------------------------------------------------------------------------
bool MC_KKEvent::Signaled(void)
{
  if(false == m_Available)
    return false;

  bool tResult = false;

  pthread_mutex_lock(&m_Mutex);

  m_State = true;
  if(true == m_ManualReset)
    tResult = (0 == pthread_cond_broadcast(&m_Cond));
  else
    tResult = (0 == pthread_cond_signal(&m_Cond));

  pthread_mutex_unlock(&m_Mutex);

    return tResult;
}
//---------------------------------------------------------------------------
bool MC_KKEvent::Nonsignaled(void)
{
  if(false == m_Available)
    return false;

  pthread_mutex_lock(&m_Mutex);
  m_State = false;
  pthread_mutex_unlock(&m_Mutex);

    return true;
}
//---------------------------------------------------------------------------
sl51314240 2014-11-10
  • 打赏
  • 举报
回复
代码都贴出来了,这没什么问题吧,环境是CentOS 6.4
zhxianbin 2014-11-10
  • 打赏
  • 举报
回复
运行中打印 tAbstime 看看
zhxianbin 2014-11-08
  • 打赏
  • 举报
回复
pthread_cond_wait 死锁了,pthread_cond_timedwait 也不会死锁,到达时间点就会解锁,会不会 tAbstime 设置有问题

23,223

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧