eventfd : 一个计数器,类似信号量;
eventfd 一般用于多线程,多进程,epoll
返回一个fd , 可读写;
write函数用于给计数器累加数值 , 例如:
// 计数器初始化 : 0
int _eventfd = eventfd(0,EFD_CLOEXEC);
uint64_t t =2;
// 写入一个2, 则此时 计数器+= 2
write(_eventfd,&t,sizeof(t));
// 再次写入一个2, 则此时 计数器是 4
write(_eventfd,&t,sizeof(t));
read 函数用于计数器递减, 当eventfd 被write后, read被唤醒,并从计数器中获取值,直到递减到0,
如果eventfd是nonblock,则返回错误,否则阻塞在read;
在上面代码后添加一行:
read(_eventfd,&t,sizeof(t));
cout << t << endl; // 4 , 此时计数器为 0,再次read将阻塞
read函数从当前的计数器中能拿多少就拿多少,
如果 写了1000次 2 ,则一次read 将读出2000;
一个多线程的例子:
int _eventfd;
int initEventFD(){
//非阻塞
_eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
return _eventfd;
}
bool wakeup(){
uint64_t t =2;
//每次增加 2
ssize_t ret = write(_eventfd,&t,sizeof(t));
return ret == sizeof(t);
}
void * write_thread(void *arg){
sleep(1);
//循环写入 1000次 2 , 计数器总计 2000
for(int i = 0; i < 1000; ++i){
wakeup();
}
cout << "write done !" << endl;
return 0;
}
void * read_thread(void *ar){
int c = 0;
while(1){
uint64_t t = 0;
// 从计数器中获取值, 并递减,
// 如果此时获取了 3, 则剩余的值为1997
ssize_t ret = read(_eventfd,&t,sizeof(t));
if(ret == sizeof(t)) {
c+= t;
cout << "read:" << t << ", count:" << c<< endl;
if(2000 == c) {
cout << "read done " << endl;
break;
}
}
}
}
int main(int argc, char *argv[])
{
initEventFD();//初始化
pthread_t t1,t2;
pthread_create(&t1,NULL,read_thread,NULL);
pthread_create(&t2,NULL,write_thread,NULL);
pthread_join(t1,0);
pthread_join(t2,0);
return 0;
}
输出:
read:6, count:1956
read:8, count:1964
read:6, count:1970
read:6, count:1976
read:8, count:1984
read:6, count:1990
read:8, count:1998
write done !read:2, count:2000
read done
还有种输出:
write done !
read:2000, count:2000
read done
结论: 你的输出极可能跟我的完全不一样,这是正常的.
第一种结果: 2个线程切换的挺平均, 因此read_thread每次都能读出一些数[ read 一次能读多少算多少,我的意思是减到0为止 ]
第2种 : write_thread 一次全执行了完了, read_thread后执行read , 一次性把所有全读取出来;
总之,不论你write了多少次,每次write的值是多少, read到最终总能读完所有的值,直到0 , 就一个计数器;
在初始化的时候还有个信号量的标志: EFD_SEMAPHORE
话说用于同步的semaphore 本来就是一个计数器 。 这个标志放在eventfd 中跟用于同步的semaphore类似,
每次递减 1 ;
上面的例子中, 可以看到每一次read 是有多少递减【拿】多少; 如果添加了这个标志,则每次递减 1;
例子:
int _eventfd;
int initEventFD(){
_eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
return _eventfd;
}
int main(int argc, char *argv[])
{
initEventFD();
uint64_t t =2;
write(_eventfd,&t,sizeof(t));
read(_eventfd,&t,sizeof(t));
cout << t << endl; // 1
read(_eventfd,&t,sizeof(t));
cout << t << endl; //1
return 0;
}
多线程的例子: 就不添加注释了, 跟上面的一个例子类似
int _eventfd;
int initEventFD(){
_eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE);
return _eventfd;
}
bool wakeup(){
uint64_t t =2;
ssize_t ret = write(_eventfd,&t,sizeof(t));
return ret == sizeof(t);
}
bool handleWakeup(){
uint64_t t = 0;
ssize_t ret = read(_eventfd,&t,sizeof(t));
return ret == sizeof(t);
}
void * write_thread(void *arg){
sleep(1);
for(int i = 0; i < 1000; ++i){
wakeup();
}
cout << "write done !" << endl;
return 0;
}
void * read_thread(void *ar){
int c = 0;
while(1){
uint64_t t = 0;
ssize_t ret = read(_eventfd,&t,sizeof(t));
if(ret == sizeof(t)) {
c+= t;
cout << "read:" << t << ", count:" << c<< endl;
if(2000 == c) {
cout << "read done " << endl;
break;
}
}
}
}
int main(int argc, char *argv[])
{
initEventFD();
pthread_t t1,t2;
pthread_create(&t1,NULL,read_thread,NULL);
pthread_create(&t2,NULL,write_thread,NULL);
pthread_join(t1,0);
pthread_join(t2,0);
return 0;
}
输出:
read:1, count:1996
read:1, count:1997
read:1, count:1998
read:1, count:1999
read:1, count:2000
read done
注意: 此时只有这样的输出是正确的, 因为信号量本身就是每次递减 1 , 所以每次 read 也只能获取 1;