多线程五共享数据

http://blog.sina.com.cn/s/blog_6572515e0100y4ll.html

    进行多线程编程,最头疼的就是那些共享的数据。因为你无法知道哪个线程会在哪个时候对它进行操作,你也无法得知那个线程会先运行,哪个线程会后运行。下面介绍一些技术,通过他们,你会合理安排你的线程之间对资源的竞争。
    1 互斥体Mutex
    2 信号灯Semophore
    3 条件变量Conditions
先说一下互斥量。
一 互斥量
    什么时候会用上互斥量了?比如你现在有一全局链表,你有几个工作线程。每一个线程从该链表中取出头节点,然后对该头节点进行处理。比如现在线程1正在取出头节点,他的操作如下:

Item * p =queue_list;
Queue_list=queue_list->next;
Process_job(p);
Free(p);
    当线程1处理完第一步,也就是Item *p=queue_list后,这时候系统停止线程1的运行,改而运行线程2。线程2照样取出头节点,然后进行处理,最后释放了该节点。过了段时间,线程1重新得到运行。而这个时候,其实p所指向的节点已经被线程2释放掉,而线程1对此毫无知晓。他会接着运行process_job(p)。而这将导致无法预料的后果!
    对于这种情况,系统给我们提供了互斥量。你在取出头节点前必须要等待互斥量,如果此时有其他线程已经获得该互斥量,那么线程将会阻塞在这个地方。只有等到其他线程释放掉该互斥量后,你的线程才有可能得到该互斥量。为什么是可能了?因为可能此时有不止你一个线程在等候该互斥量,而系统无法保证你的线程将会优先运行。
    互斥量的类型为pthread_mutex_t。你可以声明多个互斥量。在声明该变量后,你需要调用pthread_mutex_init()来初始化该变量。pthread_mutex_init的格式如下:
int  pthread_mutex_init(pthread_mutex_t  *mutex,  const  pthread_mutex_attr_t *mutexattr);
    第一个参数,mutext,也就是你之前声明的那个互斥量,第二个参数为该互斥量的属性。这个将在后面详细讨论。在创建该互斥量之后,你便可以使用它了。要得到互斥量,你需要调用下面的函数:
int pthread_mutex_lock(pthread_mutex_t *mutex);
    该函数用来给互斥量上锁,也就是我们前面所说的等待操作。互斥量一旦被上锁后,其他线程如果想给该互斥量上锁,那么就会阻塞在这个操作上。如果在此之前该互斥量已经被其他线程上锁,那么该操作将会一直阻塞在这个地方,直到获得该锁为止。在得到互斥量后,你就可以进入关键代码区了。同样,在操作完成后,你必须调用下面的函数来给互斥量解锁,也就是前面所说的释放。这样其他等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
下面给出一个简单的例子:

#include <pthread.h>
#include <iostream>
using namespace std;
struct Node {
    int data;
    struct Node* next;
    Node( int a, struct Node* prev)
    {
        data = a;
        next = NULL;
        if ( prev )
        {
            prev->next = this;
        }
    }
};
void* threadfunc1 (void* param);
void* threadfunc2 (void* param);
int main(int argc, char *argv[])
{
    Node* head = new Node( 100, NULL );
    new Node( 200, head );
    new Node( 300, head->next );
    pthread_t id1, id2;
    pthread_create( &id1, NULL, threadfunc1, (void*)head);
    pthread_create( &id2, NULL, threadfunc2, (void*)head);
    pthread_join( id1, NULL);
    pthread_join( id2, NULL);
    return 0;
}
void* threadfunc1 (void* param)
{
    Node* p = (Node*)param;
    while ( p )
    {
        if ( 200 == p->data )    
        {
            sleep(3);
            cout << "3 seconds later, in thread1, it happened : " << endl;
            cout << "p->data = " << p->data << endl;
            p->data = p->data + 22;
            cout << "p->data = " << p->data << endl;
            break;    
        }
        = p->next;
    }
}
void* threadfunc2 (void* param)
{
    Node* p = (Node*)param;
    Node* next = NULL;
    while ( p )
    {
        next = p->next;
        cout << "The deleted node data is : " << p->data << endl;
        delete p;
        = NULL;
        = next;
    }

 
程序运行出现不期望的错误:
 

[root@zieckey src]# g++ pthread_access_mem_no_mutex.cpp -lpthread
[root@zieckey src]# ./a.out
The deleted node data is : 100
The deleted node data is : 200
The deleted node data is : 300
3 seconds later, in thread1, it happened :
p->data = 167047168
p->data = 167047190
[root@zieckey src]#
甚至出现段错误:
[root@zieckey src]# g++ pthread_access_mem_no_mutex.cpp -lpthread
[root@zieckey src]# ./a.out
The deleted node data is : 100
The deleted node data is : 200
The deleted node data is : 300
3 seconds later, in thread1, it happened :
Segmentation fault
[root@zieckey src]#


上面的程序有两个线程,都共享主进程中head数据。线程一的本意是找到值为200的节点,然后对其进行一定的操作。找到后,被人为的等待了3秒钟,就在这3秒钟里,另一个线程把所有的节点都delete掉了。问题就来了。

我们可以通过互斥量来实现线程之间对同一块内存的操作。

#include <pthread.h>
#include <iostream>
#include "locker.h"
using namespace std;
CLock locker;
struct Node {
    int data;
    struct Node* next;
    
    Node( int a, struct Node* prev)
    {
        data = a;
        next = NULL;
        if ( prev )
        {
            prev->next = this;
        }
    }
};
void* threadfunc1 (void* param);
void* threadfunc2 (void* param);

int main(int argc, char *argv[])
{
    Node* head = new Node( 100, NULL );
    new Node( 200, head );
    new Node( 300, head->next );
    pthread_t id1, id2;
    pthread_create( &id1, NULL, threadfunc1, (void*)head);
    pthread_create( &id2, NULL, threadfunc2, (void*)head);
    pthread_join( id1, NULL);
    pthread_join( id2, NULL);
    return 0;
}
void* threadfunc1 (void* param)
{
    ::locker.lock();
    Node* p = (Node*)param;
    while ( p )
    {
        if ( 200 == p->data )    
        {
            sleep(3);
            cout << "3 seconds later, in thread1, it happened : " << endl;
            cout << "p->data = " << p->data << endl;
            p->data = p->data + 22;
            cout << "p->data = " << p->data << endl;
            break;    
        }
        = p->next;
    }
    ::locker.unlock();
}
void* threadfunc2 (void* param)
{
    Node* p = (Node*)param;
    Node* next = NULL;
    while ( p )
    {
        if ( !::locker.isLocked() )
        {
            next = p->next;
            cout << "The deleted node data is : " << p->data << endl;
            delete p;
            = NULL;
            = next;
        }    
        else
        {
            cout << "Oh, no, the memmery is locked, it can not be accessed. We will try later" << endl;    
            sleep(1);
        }
    }
}
运行结果:
[root@zieckey src]# g++ pthread_access_mem_with_mutex.cpp locker.cpp -lpthread
[root@zieckey src]# ./a.out
Oh, no, the memmery is locked, it can not be accessed. We will try later
Oh, no, the memmery is locked, it can not be accessed. We will try later
Oh, no, the memmery is locked, it can not be accessed. We will try later
3 seconds later, in thread1, it happened :
p->data = 200
p->data = 222
The deleted node data is : 100
The deleted node data is : 222
The deleted node data is : 300
[root@zieckey src]#
下面是CLock类,封装了对mutex的操作。

* ----------------------------------------------
//    locker.h
// ---------------------------------------------*/


#ifndef LUCKZJ_LOCKER_H
#define LUCKZJ_LOCKER_H
#include "pthread.h"
class CLock
{
    
// mutex
private:
    pthread_mutex_t mutex;
    
// ctor & dtor
public:
    CLock();
    ~CLock();
    
// public interface
public:
    int lock ();
    int unlock ();
    bool isLocked ();
    int tryLock ();
    
};
#endif

#include "locker.h"
CLock::CLock()
{
    pthread_mutex_init( &mutex, NULL );
}
int CLock::lock ()
{
     return pthread_mutex_lock (&mutex);
}
int CLock::unlock ()
{
    return pthread_mutex_unlock (&mutex);
}
bool CLock::isLocked ()
{
    if ( 0 == pthread_mutex_trylock(&mutex) )
    {
        pthread_mutex_unlock (&mutex);
        return false;
    }
    return true;
}
int CLock::tryLock()
{
    return pthread_mutex_trylock( &mutex );
}
CLock::~CLock()
{
    pthread_mutex_destroy( &mutex );
}

如果一个线程已经给一个互斥量上锁了,后来在操作的过程中又再次调用了该上锁的操作,那么该线程将会无限阻塞在这个地方,从而导致死锁。怎么变了?这就需要我们之前所提到的互斥量的属性。

互斥量分为下面三种:

    快速型。这种类型也是默认的类型。该线程的行为正如上面所说的。

    递归型。如果遇到我们上面所提到的死锁情况,同一线程循环给互斥量上锁,那么系统将会知道该上锁行为来自同一线程,那么就会同意线程给该互斥量上锁。

    错误检测型。如果该互斥量已经被上锁,那么后续的上锁将会失败而不会阻塞,pthread_mutex_lock()操作将会返回EDEADLK。

    互斥量的属性类型为pthread_mutexattr_t。声明后调用pthread_mutexattr_init()来创建该互斥量。然后调用int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);来设置属性。int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);格式如下:

int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);

第一个参数,attr,就是前面声明的属性变量,第二个参数,kind,就是我们要设置的属性类型。他有下面几个选项:

     PTHREAD_MUTEX_FAST_NP

     PTHREAD_MUTEX_RECURSIVE_NP

     PTHREAD_MUTEX_ERRORCHECK_NP

下面给出一个使用属性的简单过程:

pthread_mutex_t mutex;

pthread_mutexattr_t attr;

pthread_mutexattr_init(&attr);

pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);

pthread_mutex_init(&mutex,&attr);

pthread_mutex_destroy(&attr);

    前面我们提到在调用pthread_mutex_lock()的时候,如果此时mutex已经被其他线程上锁,那么该操作将会一直阻塞在这个地方。如果我们此时不想一直阻塞在这个地方,那么可以调用下面函数:

pthread_mutex_trylock()

    如果此时互斥量没有被上锁,那么pthread_mutex_trylock()将会返回0,并会对该互斥量上锁。如果互斥量已经被上锁,那么会立刻返回EBUSY。

   上面谈到的是使用互斥量。如果碰到下面这种情况,该怎么办了?还是上面程序中提到的工作链表。此时必然有一个生产者线程,用于往链表里添加节点。如果这一段时间没有工作,那么工作线程将会不停的调用lock,unlock操作。而这样的操作毫无疑义。

 

二 Condition Variable (条件变量) 
    也是一种用于同步的device. 允许一个进程(或线程)将自己挂起等待一个条件变量的状
被改变.
    有下列几个函数:
    int pthread_cond_init (pthread_cond_t *COND,pthread_condattr_t *cond_ATTR);
    int pthread_cond_signal (pthread_cond_t *COND);
    int pthread_cond_broadcast (pthread_cond_t *COND);
    int pthread_cond_wait (pthread_cond_t *COND, pthread_mutex_t *MUTEX);
    int pthread_cond_timedwait (pthread_cond_t *COND, pthread_mutex_t *MUTEX, const struct timespec *ABSTIME);
    int pthread_cond_destroy (pthread_cond_t *COND);
    我想看看名字就可以知道它们的用途了. 通常我们也使用静态变量来初始化一个条件变量.
    Example:
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_cond_signal 用于唤醒一个被锁定的线程.
    pthread_cond_broadcast 用于唤醒所有被锁定的线程.
    pthread_cond_wait 用于等待.
    为了解决竞争问题(即一个线程刚要去wait而另一个线程已经signal了), 它要与一个mutex连用.
    看一看下面的典型的例子。
假设x、y这两个变量被多个线程共享,我们希望这个线程一直等到x和y相等才继续执行这个线程。典型的忙等待方法如下(这是一个错误的方法):

while( x != y );

下面是使用条件变量正确的方法:

pthread使用说明 - 四叶草 - 四叶草的博客pthread_mutex_t mut 
=  PTHREAD_MUTEX_INITIALIZER;
pthread使用说明 - 四叶草 - 四叶草的博客pthread_cond_t cond 
=  PTHREAD_COND_INITIALIZER;
pthread使用说明 - 四叶草 - 四叶草的博客
pthread使用说明 - 四叶草 - 四叶草的博客
// Waiting until is the same as Y is performed as follows:
pthread使用说明 - 四叶草 - 四叶草的博客

pthread使用说明 - 四叶草 - 四叶草的博客pthread_mutex_lock(
& mut);
pthread使用说明 - 四叶草 - 四叶草的博客pthread使用说明 - 四叶草 - 四叶草的博客
while  (x  !=  y)  pthread使用说明 - 四叶草 - 四叶草的博客 {
pthread使用说明 - 四叶草 - 四叶草的博客    pthread_cond_wait(
&cond, &mut);
pthread使用说明 - 四叶草 - 四叶草的博客}

pthread使用说明 - 四叶草 - 四叶草的博客pthread使用说明 - 四叶草 - 四叶草的博客

pthread使用说明 - 四叶草 - 四叶草的博客pthread_mutex_unlock(
& mut);
pthread使用说明 - 四叶草 - 四叶草的博客

pthread_cond_wait的执行过程如下:
    1. 首先, 它unlock the mutex, then 挂起当前的线程.
    2. 当被唤醒的时候, 它会lock the mutex.
    这样就保证了这是一个临界区.


1.初始化条件变量pthread_cond_init#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cv,
const pthread_condattr_t *cattr);
返回值:函数成功返回0;任何其他返回值都表示错误

初始化一个条件变量。当参数cattr为空指针时,函数创建的是一个缺省的条件变量。否则条件变量的属性将由cattr中的属性值来决定。调用 pthread_cond_init函数时,参数cattr为空指针等价于cattr中的属性为缺省属性,只是前者不需要cattr所占用的内存开销。这个函数返回时,条件变量被存放在参数cv指向的内存中。

可以用宏PTHREAD_COND_INITIALIZER来初始化静态定义的条件变量,使其具有缺省属性。这和用pthread_cond_init函数动态分配的效果是一样的。初始化时不进行错误检查。如:

pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

不能由多个线程同时初始化一个条件变量。当需要重新初始化或释放一个条件变量时,应用程序必须保证这个条件变量未被使用。

 2.阻塞在条件变量上pthread_cond_wait

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cv,
pthread_mutex_t *mutex);
返回值:函数成功返回0;任何其他返回值都表示错误

函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上。

被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。

pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值。

pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错返回。

一般一个条件表达式都是在一个互斥锁的保护下被检查。当条件表达式未被满足时,线程将仍然阻塞在这个条件变量上。当另一个线程改变了条件的值并向条件变量发出信号时,等待在这个条件变量上的一个线程或所有线程被唤醒,接着都试图再次占有相应的互斥锁。

阻塞在条件变量上的线程被唤醒以后,直到pthread_cond_wait()函数返回之前条件的值都有可能发生变化。所以函数返回以后,在锁定相应的互斥锁之前,必须重新测试条件值。最好的测试方法是循环调用pthread_cond_wait函数,并把满足条件的表达式置为循环的终止条件。如:

pthread_mutex_lock();
while (condition_is_false)
pthread_cond_wait();
pthread_mutex_unlock();

阻塞在同一个条件变量上的不同线程被释放的次序是不一定的。

注意:pthread_cond_wait()函数是退出点,如果在调用这个函数时,已有一个挂起的退出请求,且线程允许退出,这个线程将被终止并开始执行善后处理函数,而这时和条件变量相关的互斥锁仍将处在锁定状态。

 3.解除在条件变量上的阻塞pthread_cond_signal

#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误

函数被用来释放被阻塞在指定条件变量上的一个线程。

必须在互斥锁的保护下使用相应的条件变量。否则对条件变量的解锁有可能发生在锁定条件变量之前,从而造成死锁。

唤醒阻塞在条件变量上的所有线程的顺序由调度策略决定,如果线程的调度策略是SCHED_OTHER类型的,系统将根据线程的优先级唤醒线程。

如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用。

 4.阻塞直到指定时间pthread_cond_timedwait

#include <pthread.h>
#include <time.h>
int pthread_cond_timedwait(pthread_cond_t *cv,
pthread_mutex_t *mp, const structtimespec * abstime);
返回值:函数成功返回0;任何其他返回值都表示错误

函数到了一定的时间,即使条件未发生也会解除阻塞。这个时间由参数abstime指定。函数返回时,相应的互斥锁往往是锁定的,即使是函数出错返回。

注意:pthread_cond_timedwait函数也是退出点。

超时时间参数是指一天中的某个时刻。使用举例:

pthread_timestruc_t to;
to.tv_sec = time(NULL) + TIMEOUT;
to.tv_nsec = 0;

超时返回的错误码是ETIMEDOUT。

 5.释放阻塞的所有线程pthread_cond_broadcast

#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误

函数唤醒所有被pthread_cond_wait函数阻塞在某个条件变量上的线程,参数cv被用来指定这个条件变量。当没有线程阻塞在这个条件变量上时,pthread_cond_broadcast函数无效。

由于pthread_cond_broadcast函数唤醒所有阻塞在某个条件变量上的线程,这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用pthread_cond_broadcast函数。

 6.释放条件变量pthread_cond_destroy

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误

释放条件变量。

注意:条件变量占用的空间并未被释放。

 7.唤醒丢失问题

在线程未获得相应的互斥锁时调用pthread_cond_signal或pthread_cond_broadcast函数可能会引起唤醒丢失问题。

唤醒丢失往往会在下面的情况下发生:

  1. 一个线程调用pthread_cond_signal或pthread_cond_broadcast函数;
  2. 另一个线程正处在测试条件变量和调用pthread_cond_wait函数之间;
  3. 没有线程正在处在阻塞等待的状态下。

三 Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。
    Binary semaphore与Mutex的差异:
    在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。而semaphore可以由其它进程释放(这时的semaphore实际就是个原子的变量,大家可以加或减),因此semaphore可以用于进程间同步。Semaphore的同步功能是所有系统都支持的,而Mutex能否由其他进程释放则未定,因此建议mutex只用于保护critical section。而semaphore则用于保护某变量,或者同步。
    关于semaphore和mutex的区别,网上有著名的厕所理论

    Mutex:Is a key to a toilet. One person can have the key - occupy the toilet - at the time. When finished, the person gives (frees) the key to the next person in the queue.Officially: “Mutexes are typically used to serialise access to a section of re-entrant code that cannot be executed concurrently by more than one thread. A mutex object only allows one thread into a controlled section, forcing other threads which attempt to gain access to that section to wait until the first thread has exited from that section.”Ref: Symbian Developer Library(A mutex is really a semaphore with value 1.)
    Semaphore is the number of free identical toilet keys. Example, say we have four toilets with identical locks and keys. The semaphore count - the count of keys - is set to 4 at beginning (all four toilets are free), then the count value is decremented as people are coming in. If all toilets are full, ie. there are no free keys left, the semaphore count is 0. Now, when eq. one person leaves the toilet, semaphore is increased to 1 (one free key), and given to the next person in the queue.
    Officially: “A semaphore restricts the number of simultaneous users of a shared resource up to a maximum number. Threads can request access to the resource (decrementing the semaphore), and can signal that they have finished using the resource (incrementing the semaphore).”Ref: Symbian Developer Library
    所以,mutex就是一个binary semaphore (值就是0或者1)。但是他们的区别又在哪里呢?主要有两个方面:
    * 初始状态不一样:mutex的初始值是1(表示锁available),而semaphore的初始值是0(表示unsignaled的状态)。随后的操作基本一样。mutex_lock和sem_post都把值从0变成1,mutex_unlock和sem_wait都把值从1变成0(如果值是零就等待)。初始值决定了:虽然mutex_lock和sem_wait都是执行V操作,但是sem_wait将立刻将当前线程block住,直到有其他线程post;mutex_lock在初始状态下是可以进入的。
    * 用法不一样(对称 vs. 非对称):这里说的是“用法”。Semaphore实现了signal,但是mutex也有signal(当一个线程lock后另外一个线程 unlock,lock住的线程将收到这个signal继续运行)。在mutex的使用中,模型是对称的。unlock的线程也要先lock。而 semaphore则是非对称的模型,对于一个semaphore,只有一方post,另外一方只wait。就拿上面的厕所理论来说,mutex是一个钥匙不断重复的使用,传递在各个线程之间,而semaphore择是一方不断的制造钥匙,而供另外一方使用(另外一方不用归还)。
    前面的实验证明,mutex确实能够做到post和wait的功能,只是大家不用而已,因为它是“mutex”不是semaphore。
    下面给出一个例子:
    要让一个thread在背景不断的执行,最简单的方式就是在该thread执行无穷回圈,如while(1) {},这种写法虽可行,却会让CPU飙高到100%,因为CPU一直死死的等,其实比较好的方法是,背景平时在Sleep状态,当前景呼叫背景时,背景马上被唤醒,执行该做的事,做完马上Sleep,等待前景呼叫。当背景sem_wait()时,就是马上处于Sleep状态,当前景sem_post() 时,会马上换起背景执行,如此就可避免CPU 100%的情形了。

/**//*
    (C) OOMusou 2006 http://oomusou.cnblogs.com

    Filename : pthread_create_semaphore.cpp
    Compiler : gcc 4.10 on Fedora 5 / gcc 3.4 on Cygwin 1.5.21
    Description : Demo how to create thread with semaphore in Linux.
    Release : 12/03/2006
    Compile : g++ -lpthread pthread_create_semaphore.cpp
    */
#include <stdio.h> // printf(),
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <pthread.h> // pthread_create(), pthread_join()
#include <semaphore.h> // sem_init()

sem_t binSem;

void* helloWorld(void* arg);

int main() {
    // Result for System call
    int res = 0;

    // Initialize semaphore
    res = sem_init(&binSem, 0, 0);
    if (res) {
        printf("Semaphore initialization failed!!\n");
        exit(EXIT_FAILURE);
    }

    // Create thread
    pthread_t thdHelloWorld;
    res = pthread_create(&thdHelloWorld, NULL, helloWorld, NULL);
    if (res) {
        printf("Thread creation failed!!\n");
        exit(EXIT_FAILURE);
    }

    while(1) {
        // Post semaphore
        sem_post(&binSem);
        printf("In main, sleep several seconds.\n");
        sleep(1);
    }

    // Wait for thread synchronization
    void *threadResult;
    res = pthread_join(thdHelloWorld, &threadResult);
    if (res) {
        printf("Thread join failed!!\n");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}

void* helloWorld(void* arg) {
    while(1) {
        // Wait semaphore
        sem_wait(&binSem);
        printf("Hello World\n");
    }
}


编译运行:

[root@localhost semaphore]# gcc semaphore.-lpthread
[root@localhost semaphore]# ./a.out
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值