`
lijuanabc
  • 浏览: 125818 次
社区版块
存档分类
最新评论

随笔之POSIX cond和Windows同步对象Event的讨论

 
阅读更多
一 缘由
最近在实现一个线程池的时候,需要用到POSIX中的cond和mutex进行线程间等待和同步,功能类似MS的同步对象Event。
发现cond和mutex的连用还是挺不人性化的。说实话,MS在同步对象的API上,做得还是相当不错,文档也很清晰。
Anyway,既然只能使用POSIX,就只能将就了。
我这个线程池在实现中碰到以下2个问题:
1 有n个线程等待一个事件。当有任务添加的时候,需要触发其中一个线程启动。
2 当线程池退出时,我需要触发所有线程启动,并检测退出标志,从而退出线程循环。
这个问题其实比初看上去要复杂,下面来分析
二 Windows上的实现
先介绍下Event同步对象,
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,// pointer to security attributes
BOOL bManualReset, // flag for manual-reset event
BOOL bInitialState, // flag for initial state
LPCTSTR lpName // pointer to event-object name
);
第二个参数:bManualReset表示该对象为人工还是自动变量。我们重点就是讨论这个,该值决定以下几个特点:
  • 当Event为人工变量时,一旦被触发,则一直保持触发状态,触发调用ResetEvent重置状态。假设该值被触发,那么调用WaitXXX函数的所有线程都不应该阻塞在该事件上。
  • 当Event为自动变量时,一旦被触发,如果有线程在等,那么只会启动其中一个线程(只会启动一个线程,因为该值在启动一个线程后会重置)。如果没有线程在等,那么该值一直保持触发状态
从上面Event的介绍来看,自动变量一次触发一个线程,而人工变量一直保持触发状态。不过系统并没有说如果一个线程很快SetEvent并又ResetEvent的话,Wait的线程会如何。
我们先看看在MS平台上该如何解决上面的问题:
最普遍的方法就是:
1 创建一个Auto的Event,这个Event用来触发工作线程从任务队列中获取任务
2 创建一个Manual的Event,这个Event用来触发所有工作线程退出
然后利用WaitForMultiObject来等待这两个Event。
Problem solved!!
三 Linux的实现
Linux上最大的问题是没有WaitForMultiObject这样的函数,那么我们只能创建一个包含Mutex和Cond的结构体来充当Event
由于cond的触发有两个函数,特性分别是:
  • pthread_cond_signal:保证多个线程调用pthread_cond_wait等待的时候只有一个线程能够返回。
  • pthread_cond_broadcast:保证多个线程等待的时候,都能返回。
我们在使用cond实现类型的触发和等待的时候,代码经常如下所示:
void Wake(allThreads?)
{
Mutex lock
set condition = 1;
if(allThreads)
pthread_cond_broadcast //触发所有线程
else
pthread_cond_signal //触发单个线程
Mutex unlock
}
void Wait()
{
Mutex lock
while(condition != 1)
{
pthread_cond_wait(cond,&mutex); //cond的wait需要传入一个Mutex做参数,wait函数内部会unlock这个mutex
}
Mutex unlock
}
为什么在Wait的时候需要有一个while循环来检测condition是否满足条件呢?最主要有两个原因:
  • cond本身的机制导致。如果cond触发(不论是signal还是broadcast)的时候,没有线程在wait的话,那么线程以后wait将无法捕获这个触发。这和Windows的Event截然不同。自动变量的Event如果触发后,即使当时没有线程在等待,如果以后有线程等待的话,那么也是可以返回的。而posix的cond没有实现这个功能。所以我们只能自己加condition来做判断。如果condition为1,则无需等待。
  • pthread_cond_wait的返回有可能是因为信号的原因导致,这个时候conditon并不满足,所以这里也需要一个while循环
根据上面的基础知识,我们目前已满足的是:
  • cond_signal能并且只能触发一个线程起来
  • cond_broadcast能触发所有线程起来
但是问题随之而来,因为现在只有一个condition,什么时候置为零呢?必须满足两个条件:
  • 对于触发单个线程的来说,因为只有一个启动,所以当它启动后,需要把condtion置为零,表示自己已经消费了这个条件。
  • 对于所有启动的来说,只有最后一个线程退出等待的时候需要把conditon置为零,表示所有人都消费了这个条件。
解决办法就是使用waiter计数,最终修改后的代码如下:
void Wake(allThreads?)
{
Mutex lock
if(condtion == -1 || condition == 1) //为什么要加这样的判断?
{
Mutex unlock
return;
}
if(allThreads)
{
set condition = -1; //表示等待多个
pthread_cond_broadcast //触发所有线程
}
else
{
set condition = 1;
pthread_cond_signal //触发单个线程
}
Mutex unlock
}
void Wait()
{
Mutex lock
++waiters;
while(condition == 0)
{
pthread_cond_wait(cond,&mutex); //cond的wait需要传入一个Mutex做参数,wait函数内部会unlock这个mutex
}
if(--waiters == 0)
condition = 0;
Mutex unlock
}
我们可以捋一下各种case:
  • 只触发单个线程的话,没有问题。这个是最简单的。
  • 触发多个线程的话,如果在触发后并且在旧的等待线程未全部返还之前,如果又有新线程调用wait的话,实际上是不会被阻塞的。
Wake函数前面红色字体的代码干什么用?
就怕调用者先调用了Wake(true),旋即又调用了Wake(false),导致condtion值混乱。通过判断condition是否已经触发,我们可以避免出问题。
四:结论
POSIX的cond和mutex联合使用,总感觉效率上会有损失。因为cond_wait返还后还是需要竞争mutex,实际上Wait函数在用户空间是串行执行的。
不知道Windows的Event是如何实现的?
上面的代码应该还不是很完善,有问题再修改,不知道您看出什么毛病了吗?
分享到:
评论

相关推荐

    windows posix 线程库

    本文将详细讨论“Windows POSIX线程库”及其在Windows环境下实现POSIX线程(pthreads)的功能,以及如何利用该库实现Linux多线程代码在Windows上的运行。 POSIX线程库,通常称为pthreads,是UNIX系统中广泛使用的多...

    posix pthread windows 实现

    - `pthread_cond_*()`: 条件变量,用于线程间的同步和等待特定条件。 - `pthread_rwlock_*()`: 读写锁,允许多个读取者同时访问资源,但只允许一个写入者。 **4. 多线程编程的关键点** - 资源共享:线程之间共享...

    POSIX Threads (pthreads) for Win32

    例如`pthread_create()`用于创建新线程,`pthread_join()`用于等待线程结束,以及`pthread_mutex_lock()`和`pthread_rwlock_rdlock()`等用于线程同步和互斥的函数。 在pthreads-win32 2.9.1这个版本中,包含了以下...

    用于windows的posix regex C库

    POSIX(Portable Operating System Interface)正则表达式是符合POSIX标准的一套正则表达式语法,它在多个操作系统上,包括Linux和Unix,被广泛使用。在Windows系统中,由于其内核与Linux不同,通常不直接支持POSIX...

    POSIX Threads for Windows-开源

    Windows的POSIX线程也称为“ pthreads-win32”,实现了Single Unix Specification版本3中与线程相关的API的很大一部分。一致性和质量是此成熟库的高度优先事项。 开发工作始于1998年,并一直持续进行,并做出了许多...

    POSIX多线程程序设计.pdf

    然后讨论了一些高级话题,包括属性对象、线程私有数据和实时调度。此外,本书还讨论了调度的问题,并给出了避免错误和提高性能等问题的有价值的建议。本书使用了大量注释过后 实例来解释实际的概念,并包括Pthreads...

    POSIX多线程程序设计

    然后讨论了, 一些高级话题,包括属性对象、线程私有数据和实时调度。此外,本书还讨论了调度的问, 题,并给出了避免错误和提高性能等问题的有价值的建议。本书使用了大量注释过的实例, 来解释实际的概念,并包括...

    Posix(1).rar_POSIX Pthread_posix_pthread_pthread posix

    4. **同步机制**:pthreads提供多种同步原语,包括互斥锁(`pthread_mutex_t`)、条件变量(`pthread_cond_t`)、读写锁(`pthread_rwlock_t`)和信号量(`sem_t`),用于控制对共享资源的访问。 5. **线程调度**:...

    POSIX API多线程程序设计中文版和源码

    通过分析和运行源码,你可以了解多线程在并发执行、数据共享、同步和通信等方面的实现细节。 此外,中文版教程将帮助中文用户更容易理解复杂的线程编程概念,避免语言障碍。结合理论与实践,你可以逐步提升在多线程...

    Posix线程同步机制及其在电网监控系统中的应用借鉴.pdf

    本文对 Posix 线程同步机制进行了详细的分析和讨论,并结合分布式电网监控系统的应用实例进行了阐述。 Posix 线程同步机制的重要性主要体现在以下几个方面: 首先, Posix 线程同步机制可以解决任务间需要大量...

    posix和c11都实现的跨平台线程库

    pthread是这个标准中的一部分,提供了创建、同步和管理线程的API。在Linux和其他类UNIX系统中,pthread是默认的线程库,支持线程的创建、销毁、同步(如互斥锁、条件变量)等功能。然而,Windows系统并不原生支持...

    Posix.rar_posix

    在实际项目中,理解线程间的通信、同步和资源管理,以及如何避免死锁和数据竞争,都是至关重要的技能。这份“Posix.doc”文档很可能是对这些概念和用法的详细解释和示例,对于想要深入学习Linux多线程编程的开发者来...

    Posix线程编程指南

    线程同步是Posix线程编程中的核心内容,包括互斥锁(`pthread_mutex_t`)、条件变量(`pthread_cond_t`)、信号量(`sem_t`)和读写锁(`pthread_rwlock_t`)。互斥锁用于保护临界区,确保同一时间只有一个线程能...

    pthread_cond_wait() 用法深入分析

    总结来说,`pthread_cond_wait()` 是一个强大的工具,用于实现线程间的同步和协作。正确使用它可以有效地避免竞争条件,提高多线程程序的效率和可靠性。但是,不当使用可能会导致死锁或其他并发问题,因此在编程时...

    (中英文)-POSIX多线程程序设计-Programming with POSIX Threads

    - **条件变量**:`pthread_cond_t`配合互斥锁实现复杂同步,通过`pthread_cond_wait()`和`pthread_cond_signal()`或`pthread_cond_broadcast()`控制线程的等待和唤醒。 - **信号量**:`sem_t`提供了计数型信号量,...

    windows 与linux 进程间通信区别

    具体到同步对象的类型,Windows提供了有名(Named)和无名(Unnamed)两种信号量,前者可以用于跨进程的同步,而后者仅限于同一进程内的同步。有名信号量是基于全局命名空间的,而无名信号量则仅在创建它们的进程内...

    POSIX线程程序设计

    主要函数有`pthread_cond_init()`, `pthread_cond_wait()`, 和 `pthread_cond_signal()`。 3. 信号量:一种更灵活的同步机制,可以控制多个线程的访问数量。`sem_open()`, `sem_post()`, 和 `sem_wait()`是常用的...

    windows下可用的pthread库

    pthread库是基于Posix标准的线程API,提供了创建、同步、通信和管理线程等功能。它包括了线程的创建(pthread_create)、线程的退出(pthread_exit)、线程的等待(pthread_join)以及线程的属性设置等接口。此外,...

    POSIX Threads Programming.pdf

    - **创建和销毁互斥量**:`pthread_mutex_init()`和`pthread_mutex_destroy()`函数用于初始化和销毁互斥量对象。 - **锁定和解锁互斥量**:`pthread_mutex_lock()`和`pthread_mutex_unlock()`函数用于锁定和解锁互斥...

Global site tag (gtag.js) - Google Analytics