生产者线程用于往链表里添加节点,数个工作线程从链表取出节点并处理。对于一般的mutex_lock,mutex_unlock方式,如果这一段时间没有工作,那么工作线程将会不停的调用lock,unlock操作。而这样的操作毫无疑义。
在这里系统给我们提供了另外一种同步机制,信号灯,Semaphore。
信号灯其实就是一个计数器,也是一个整数。每一次调用wait操作将会使semaphore值减一,而如果semaphore值已经为0,则wait操作将会阻塞。每一次调用post操作将会使semaphore值加一。将这些操作用到上面的问题中。工作线程每一次调用wait操作,如果此时链表中没有节点,则工作线程将会阻塞,直到链表中有节点。生产者线程在每次往链表中添加节点后调用post操作,信号灯值会加一。这样阻塞的工作线程就会停止阻塞,继续往下执行。
信号灯的类型为sem_t。在声明后必须调用sem_init()。需要传递两个参数,第一个参数就是你之前声明的sem_t变量,第二个必须为0。当你不再需要信号灯时,你必须调用sem_destroy()来释放资源。
等待信号灯的操作为sem_wait()。投递一个信号的操作为sem_post()。和互斥量一样,等待信号灯也有一个非阻塞的操作,sem_trywait()。该操作在没有信号灯的时候返回EAGAIN。
下面是一个结合了互斥量和信号灯的例子:
- #include <malloc.h>
- #include <pthread.h>
- #include <semaphore.h>
- struct job {
- /* Link field for linked list. */
- struct job* next;
- /* Other fields describing work to be done... */
- };
- /* A linked list of pending jobs. */
- struct job* job_queue;
- /* A mutex protecting job_queue. */
- pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
- /* A semaphore counting the number of jobs in the queue. */
- sem_t job_queue_count;
- /* Perform one-time initialization of the job queue. */
- void initialize_job_queue ()
- {
- /* The queue is initially empty. */
- job_queue = NULL;
- /* Initialize the semaphore which counts jobs in the queue. Its
- initial value should be zero. */
- sem_init (&job_queue_count, 0, 0);
- }
- /* Process queued jobs until the queue is empty. */
- void* thread_function (void* arg)
- {
- while (1) {
- struct job* next_job;
- /* Wait on the job queue semaphore. If its value is positive,
- indicating that the queue is not empty, decrement the count by
- 1. If the queue is empty, block until a new job is enqueued. */
- sem_wait (&job_queue_count);
- /* Lock the mutex on the job queue. */
- pthread_mutex_lock (&job_queue_mutex);
- /* Because of the semaphore, we know the queue is not empty. Get
- the next available job. */
- next_job = job_queue;
- /* Remove this job from the list. */
- job_queue = job_queue->next;
- /* Unlock the mutex on the job queue because we’re done with the
- queue for now. */
- pthread_mutex_unlock (&job_queue_mutex);
- /* Carry out the work. */
- process_job (next_job);
- /* Clean up. */
- free (next_job);
- }
- return NULL;
- }
- /* Add a new job to the front of the job queue. */
- void enqueue_job (/* Pass job-specific data here... */)
- {
- struct job* new_job;
- /* Allocate a new job object. */
- new_job = (struct job*) malloc (sizeof (struct job));
- /* Set the other fields of the job struct here... */
- /* Lock the mutex on the job queue before accessing it. */
- pthread_mutex_lock (&job_queue_mutex);
- /* Place the new job at the head of the queue. */
- new_job->next = job_queue;
- job_queue = new_job;
- /* Post to the semaphore to indicate that another job is available. If
- threads are blocked, waiting on the semaphore, one will become
- unblocked so it can process the job. */
- sem_post (&job_queue_count);
- /* Unlock the job queue mutex. */
- pthread_mutex_unlock (&job_queue_mutex);
- }