为什么 semaphore 和 event variables 是不好的?

anrxhzh 2002-06-19 10:42:02
研究了一下Boost.Threads。文档中指出 semaphore 是容易出错的,不提倡使用,而对event variables 则更是明确地禁止使用。我能够理解用 mutex 和 condition variables 可以实现 semaphore 和 event variables 的功能,但是不是很明白为什么semaphore 和 event variables 是不好的。希望有经验的人给予指点。
...全文
285 点赞 收藏 27
写回复
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
huxw 2002-07-17
boost::thread的实现显然是以posix线程标准为蓝本开发的,所以我觉得用win32下面的观点来判断是不合适的。 ;) 例如semaphore这样东西,用condition和mutex是直接可以简单实现的,posix标准中提到增加一个标准semaphore主要是处于方便和历史的考虑。

实际上,作为ipc同步原语,mutex和condition是最基本的。而各个平台下面,相同的原语名称却往往有不同的意义,这才是造成错误最大的根源,我会在文章中给一些说明。

至于实现嘛,boost::thread中的condition在win32中就是用semaphore实现的,这个也让我很担心它的效率(当然还没有测试)。反观unix下的实现,基本上是pthread_xxx系列函数的简单封装,真是比较爽啊。
回复
huxw 2002-07-17
boost::thread的实现显然是以posix线程标准为蓝本开发的,所以我觉得用win32下面的观点来判断是不合适的。 ;) 例如semaphore这样东西,用condition和mutex是直接可以简单实现的,posix标准中提到增加一个标准semaphore主要是处于方便和历史的考虑。

实际上,作为ipc同步原语,mutex和condition是最基本的。而各个平台下面,相同的原语名称却往往有不同的意义,这才是造成错误最大的根源,我会在文章中给一些说明。

至于实现嘛,boost::thread中的condition在win32中就是用semaphore实现的,这个也让我很担心它的效率(当然还没有测试)。反观unix下的实现,基本上是pthread_xxx系列函数的简单封装,真是比较爽啊。
回复
wangran 2002-07-16
不好意思,上面一句话打错了,应该是“semaphore在Win32中不常被使用”,把“不”错打为“被”了。另外,多线程我刚开始看,不是很明白,但是我想有很多很类似的事物只能从细节上去分,否则大体上的功能是一样的,引如一个新的机制就没什么意义了,是不是?
回复
anrxhzh 2002-07-15
豹子:

你的发言有自相矛盾之处:“在Win32中semaphores被使用的很少”,“semaphore在Win32中被常被使用”。
我想,semaphores 的本意是event的细化,如果说event是广播消息,semaphores就是固定大小的消息队列。semaphores到底有什么缺陷呢,你讲的
‘wait abandoned’似乎是实现的细节,不足为凭吧。
回复
wangran 2002-07-13
《Win32多线程程序设计》上面说“在许多系统中,semaphores常被使用,因为mutexes可能并不存在,在Win32中semaphores被使用的很少,因为mutex存在的缘故。”
之所以不用semaphore而用mutex,是因为“semaphore不像mutex,它并没有所谓的‘wait abandoned’状态可以被其他线程侦测到。”
WAIT_ANANDONED_0是如果一个拥有mutex的线程在结束前没有调用ReleaseMutex(),mutex不会被摧毁。该mutex会被视为“未被拥有”以及“未被激发”,而下一个等待中的线程会被以WAIT_ANANDONED_0通知。并且,mutex是semaphore的一种退化。如果你产生一个semaphore并令最大值为1,那就是一个mutex.也因此,mutex又常被称为binary semaphore.所以,semaphore在Win32中被常被使用。
关于Event,是因为“如果你对着一个event对象调用PulseEvent()并且没有现成正在等待,这个event会被遗失。”
回复
wangran 2002-07-13
《Win32多线程程序设计》上有,太晚了,明天再写上去吧。
回复
allen95 2002-07-11
gz
回复
anrxhzh 2002-07-11
to huxw:

敬候佳音。
回复
daehappy 2002-07-07
up!gz!
回复
LeoKing 2002-07-06
gz
回复
huxw 2002-07-06
呵呵,我翻译thread那部分文档的时候,也考虑过这个问题。boost::thread中的event指的主要是抽象的异步通讯的手段,具体的例如unix下面的signal。由于event虽然是可靠的,但是并不保证时效,所以它非常容易造成重入错误。更糟糕的,这样的错误很难检测,unix平台下很多系统漏洞就是这样造成的。

这是其中一个理由。我试图比较一下Ada, java, C++(主要是boost::thread)的多任务概念,以及在语言设计中的体现,更多的内容等写完了一并贴吧。 ;)
回复
anrxhzh 2002-06-21
看来这个问题不是通过论坛能讲明白的,需要一本专著来探讨。
不过有一点应该是明确的:CVs是简单清晰容易理解的。Event如果不是存在本质上的缺陷的话,至少也是被滥用的。
回复
anrxhzh 2002-06-20
//用 mutex 和 condition variables 实现循环有界缓冲区

#include <iostream>
#include <vector>
#include <boost/utility.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>

class bounded_buffer : private boost::noncopyable
{
public:
typedef boost::mutex::scoped_lock lock;

bounded_buffer(int n) : begin(0), end(0), buffered(0), circular_buf(n) { }

void send (int m) {
lock lk(monitor);
while (buffered == circular_buf.size())
buffer_not_full.wait(lk);
circular_buf[end] = m;
end = (end+1) % circular_buf.size();
++buffered;
buffer_not_empty.notify_one();
}
int receive() {
lock lk(monitor);
while (buffered == 0)
buffer_not_empty.wait(lk);
int i = circular_buf[begin];
begin = (begin+1) % circular_buf.size();
--buffered;
buffer_not_full.notify_one();
return i;
}

private:
int begin, end, buffered;
std::vector<int> circular_buf;
boost::condition buffer_not_full, buffer_not_empty;
boost::mutex monitor;
};

bounded_buffer buf(2);
boost::mutex io;

void sender() {
int n = 0;
while (n < 100) {
buf.send(n);
{
boost::mutex::scoped_lock lk(io);
std::cout << "sent: " << n << std::endl;
}
++n;
}
buf.send(-1);
}

void receiver() {
int n;
do {
n = buf.receive();
{
boost::mutex::scoped_lock lk(io);
std::cout << "received: " << n << std::endl;
}
} while (n != -1); // -1 indicates end of buffer
}

int main(int, char*[])
{
boost::thread thrd1(&sender);
boost::thread thrd2(&receiver);
thrd1.join();
thrd2.join();
return 0;
}
//output
//sent: 0
//received: 0
//received: 1
//sent: 1
//received: 2
//sent: 2
//received: 3
//sent: 3
//sent: 4
//sent: 5
//received: 4
//received: 5
//sent: 6
//sent: 7
//sent: 8
//received: 6
//received: 7
//received: 8
//sent: 9
//sent: 10
//sent: 11
//received: 9
//received: 10
//sent: 12
//sent: 13
//received: 11
//sent: 14
//received: 12
//received: 13
//sent: 15
//sent: 16
//received: 14
//received: 15
//sent: 17
//sent: 18
//received: 16
//sent: 19
//received: 17
//sent: 20
//received: 18
//sent: 21
//received: 19
//received: 20
//sent: 22
//sent: 23
//received: 21
//sent: 24
//received: 22
//sent: 25
//received: 23
//sent: 26
//received: 24
//sent: 27
//received: 25
//sent: 28
//received: 26
//received: 27
//sent: 29
//sent: 30
//received: 28
//sent: 31
//received: 29
//sent: 32
//received: 30
//sent: 33
//received: 31
//sent: 34
//received: 32
//sent: 35
//received: 33
//sent: 36
//received: 34
//sent: 37
//received: 35
//sent: 38
//received: 36
//sent: 39
//received: 37
//sent: 40
//received: 38
//received: 39
//received: 40
//received: 41
//sent: 41
//sent: 42
//sent: 43
//received: 42
//received: 43
//sent: 44
//sent: 45
//sent: 46
//received: 44
//received: 45
//sent: 47
//sent: 48
//received: 46
//sent: 49
//received: 47
//received: 48
//sent: 50
//sent: 51
//received: 49
//received: 50
//sent: 52
//sent: 53
//received: 51
//sent: 54
//received: 52
//received: 53
//sent: 55
//sent: 56
//received: 54
//received: 55
//sent: 57
//sent: 58
//received: 56
//sent: 59
//received: 57
//sent: 60
//received: 58
//sent: 61
//received: 59
//received: 60
//sent: 62
//sent: 63
//received: 61
//sent: 64
//received: 62
//sent: 65
//received: 63
//sent: 66
//received: 64
//sent: 67
//received: 65
//sent: 68
//received: 66
//received: 67
//sent: 69
//sent: 70
//received: 68
//sent: 71
//received: 69
//sent: 72
//received: 70
//sent: 73
//received: 71
//received: 72
//received: 73
//received: 74
//sent: 74
//sent: 75
//sent: 76
//received: 75
//received: 76
//sent: 77
//sent: 78
//sent: 79
//received: 77
//received: 78
//sent: 80
//sent: 81
//received: 79
//received: 80
//received: 81
//sent: 82
//sent: 83
//sent: 84
//received: 82
//received: 83
//sent: 85
//sent: 86
//received: 84
//sent: 87
//received: 85
//sent: 88
//received: 86
//sent: 89
//received: 87
//received: 88
//sent: 90
//sent: 91
//received: 89
//sent: 92
//received: 90
//sent: 93
//received: 91
//sent: 94
//received: 92
//sent: 95
//received: 93
//sent: 96
//received: 94
//received: 95
//sent: 97
//sent: 98
//received: 96
//sent: 99
//received: 97
//received: 98
//received: 99
//received: -1
回复
anrxhzh 2002-06-20
//多生产者多消费者模型

#include <iostream>
#include <vector>
#include <boost/utility.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>

int id(0);

class bounded_buffer : private boost::noncopyable
{
public:
typedef boost::mutex::scoped_lock lock;

bounded_buffer(int n) : begin(0), end(0), buffered(0), circular_buf(n) { }

void send (int m) {
lock lk(monitor);
while (buffered == circular_buf.size())
buffer_not_full.wait(lk);
if(m!=-1) m=id++;
circular_buf[end] = m;
end = (end+1) % circular_buf.size();
++buffered;
std::cout << "sent : " << m << std::endl;
buffer_not_empty.notify_one();
}
int receive() {
lock lk(monitor);
while (buffered == 0)
buffer_not_empty.wait(lk);
int i = circular_buf[begin];
begin = (begin+1) % circular_buf.size();
--buffered;
std::cout << "received: " << i << std::endl;
buffer_not_full.notify_one();
return i;
}

private:
int begin, end, buffered;
std::vector<int> circular_buf;
boost::condition buffer_not_full, buffer_not_empty;
boost::mutex monitor;
};

bounded_buffer buf(2);
const int producer_num(3),consumer_num(3),per_producer(10);

void sender() {
int n = 0;
while (n < per_producer) {
buf.send(n);
++n;
}
buf.send(-1);
}

void receiver() {
int n;
do {
n = buf.receive();
} while (n != -1); // -1 indicates end of buffer
}

int main(int, char*[])
{
boost::thread_group producers,consumers;
for (int i = 0; i < producer_num; ++i)
producers.create_thread(&sender);
for (i = 0; i < consumer_num; ++i)
consumers.create_thread(&receiver);
producers.join_all();
consumers.join_all();
return 0;
}
//output
//sent : 0
//sent : 1
//received: 0
//sent : 2
//received: 1
//sent : 3
//received: 2
//sent : 4
//received: 3
//received: 4
//sent : 5
//received: 5
//sent : 6
//received: 6
//sent : 7
//received: 7
//sent : 8
//received: 8
//sent : 9
//sent : 10
//received: 9
//received: 10
//sent : 11
//received: 11
//sent : 12
//received: 12
//sent : 13
//received: 13
//sent : 14
//received: 14
//sent : 15
//received: 15
//sent : 16
//received: 16
//sent : 17
//received: 17
//sent : 18
//received: 18
//sent : 19
//received: 19
//sent : 20
//sent : 21
//received: 20
//received: 21
//sent : 22
//received: 22
//sent : 23
//received: 23
//sent : 24
//received: 24
//sent : 25
//sent : 26
//received: 25
//received: 26
//sent : 27
//sent : 28
//received: 27
//received: 28
//sent : -1
//received: -1
//sent : -1
//received: -1
//sent : 29
//received: 29
//sent : -1
//received: -1
回复
anrxhzh 2002-06-20
Boost.Threads 的文档在这里:
http://www.boost.org/libs/thread/doc/index.html
回复
hello_wyq 2002-06-20
楼上的,是条件变量呀,操作系统里有一堆这个方面的解释。

回复
hello_wyq 2002-06-20
楼上的,是条件变量呀,操作系统里有一堆这个方面的解释。

回复
xrbeck 2002-06-20
还一个你提到的"condition variables "是什么..
是不是哪个critical section..
回复
xrbeck 2002-06-20
你在什么地方看到的什么文章?给个连接我去看看..
什么什么"event"明确禁止.这那门子的事啊.多少
程序(比如说串口)都用到了event啊..
回复
repus 2002-06-20
胡说呗,只能说某个系统的Semaphore实现得不好,容易出错。
一棍子打死一船人,显然是胡说。
回复
发动态
发帖子
C语言
创建于2007-09-28

6.3w+

社区成员

C语言相关问题讨论
申请成为版主
社区公告
暂无公告