2012-03-28 56 views
2

我一直试图围绕C语言中信号量的概念进行包装,并且我取得了一些有限的成功。据我所知,在C中,如果一个信号量的值为0,sem_wait()应导致该线程阻塞,直到该信号量的值不再为0.意外的信号量行为,超出sem_wait执行的线程()

我的问题是这样的:一些非常快速的示例代码(下面),我不确定为什么,但是一旦线程被创建,他们似乎会执行超出sem_wait()的代码,即使信号量的值看起来是0.我不知道为什么这将是。

编辑:根据Perception的建议,我检查了sem_wait()的返回值,它似乎将errno设置为“Operation Timed Out”。据我所知,这不应该发生,除非我使用sem_timedwait()。仍在挖...

编辑2:Oop。应该更仔细地阅读我的输出。它实际上将其设置为“功能未实现”。

#include <stdio.h> 
#include <unistd.h> 
#include <pthread.h> 
#include <semaphore.h> 
#include <errno.h> 

// vars 
int jobsInQueue, currentJob; 
sem_t *semaphore; 
pthread_t threads[10]; 
int runningThreads = 0; 

// prototypes 
void *do_work(void *arg); 
void add_job(); 

int main() 
{ 
    // i for the for loop used to create the threads 
    int i; 

    // counter for jobs in the queue 
    jobsInQueue = 0; 

    // indicator for the current job 
    currentJob = 0; 

    // indicator for whether we have reached the limit imposed in the while loop used for adding jobs 
    int reachedlimit = 0; 

    // create the semaphore 
    semaphore = sem_open("semaphore", O_CREAT, 0600, 0); 

    // get the value of the semaphore and temporarily store it in reachedlimit 
    sem_getvalue(semaphore, &reachedlimit); 

    // print off the value of the semaphore. I think I'm crazy because the threads are executing code 
    // before the semaphore is posted to, but this appears to be zero... 
    fprintf(stderr, "semaphore: %d", reachedlimit); 
    fflush(stderr); 

    // set reachedlimit back to zero because we expect it to be zero below 
    reachedlimit = 0; 

    for(i = 0; i < 10; ++i) 
    { 
     // create a pthread 
     pthread_create(&threads[i], NULL, &do_work, (void *)i); 

     // increment the number of running threads 
     runningThreads++; 
    } 

    // sleep for a couple of seconds just as separator space 
    sleep(2); 

    // while there are threads running 
    while(runningThreads > 0) 
    { 
     // sleep for a tenth of a second 
     usleep(100000); 

     // after that, if there are 1000 or more jobs in the queue, we've reached the number of total jobs we want 
     if(jobsInQueue >= 1000) reachedlimit = 1; 

     // if we haven't reached that, then add another job 
     if(reachedlimit == 0) add_job(); 

     // print that we're still sleeping and the number of jobs in the queue. 
     fprintf(stderr, "Still sleeping. Jobs in queue: %d\n", jobsInQueue); 
     fflush(stderr); 
    } 
} 

void *do_work(void *arg) 
{ 
    // when the thread is created, print this thread's number to the console 
    fprintf(stderr, "I am thread %d.\n", (int)arg); 
    fflush(stderr); 

    // then loop infinitely doing the following... 
    while(1) 
    { 
     // wait until the semaphore's value is no longer zero <-- doesn't seem to do this 
     sem_wait(semaphore); 

     // if we are on the 1000th job, terminate the thread 
     if (currentJob >= 1000) { 
      runningThreads--; 
      fprintf(stderr, "Thread %d terminated", (int)arg); 
      fflush(stderr); 
      pthread_exit((void *)1); 
     } 

     // otherwise, increment the current job counter 
     currentJob++; 

     // tell the console that this thread took a job 
     fprintf(stderr, "Thread %d: I took a job.: %d\n", (int)arg, currentJob); 
     fflush(stderr); 

     // subtract one from the count of jobs in the queue 
     jobsInQueue--; 

     // sleep for at least one second before taking another job 
     sleep(1); 
    } 

    // this will never happen because the while loop will never be broken 
    runningThreads--; 
    return NULL; 
} 

void add_job() 
{ 
    // increment the count of jobs in the queue 
    jobsInQueue++; 

    // print that a job has been added 
    fprintf(stderr, "Job added\n"); 
    fflush(stderr); 

    // post to the semaphore, which should essentially release the job for "processing" if I understand correctly. 
    sem_post(semaphore); 
} 

一些样本输出:一个过程之后

semaphore: 0 
I am thread 0. 
I am thread 1. 
Thread 0: I took a job.: 1 
I am thread 2. 
I am thread 3. 
Thread 1: I took a job.: 2 
I am thread 4. 
I am thread 5. 
I am thread 6. 
Thread 2: I took a job.: 3 
I am thread 7. 
I am thread 8. 
Thread 3: I took a job.: 4 
I am thread 9. 
Thread 4: I took a job.: 5 
Thread 5: I took a job.: 6 
Thread 6: I took a job.: 7 
Thread 7: I took a job.: 8 
Thread 8: I took a job.: 9 
Thread 9: I took a job.: 10 
Thread 0: I took a job.: 12 
Thread 4: I took a job.: 11 
Thread 5: I took a job.: 13 
Thread 6: I took a job.: 14 
Thread 1: I took a job.: 15 
Thread 8: I took a job.: 17 
Thread 3: I took a job.: 16 
Thread 7: I took a job.: 18 
Thread 2: I took a job.: 19 
Thread 9: I took a job.: 20 
Thread 0: I took a job.: 21 
Thread 1: I took a job.: 22 
Thread 8: I took a job.: 23 
Thread 3: I took a job.: 24 
Thread 5: I took a job.: 25 
Thread 7: I took a job.: 26 
Thread 6: I took a job.: 27 
Thread 2: I took a job.: 29 
Thread 4: I took a job.: 28 
Thread 9: I took a job.: 30 
Job added 
Still sleeping. Jobs in queue: -29 
Job added 
Still sleeping. Jobs in queue: -28 
Job added 
Still sleeping. Jobs in queue: -27 
Job added 
Still sleeping. Jobs in queue: -26 
Job added 
Still sleeping. Jobs in queue: -25 
Job added 
Still sleeping. Jobs in queue: -24 
Job added 
Still sleeping. Jobs in queue: -23 
Job added 
Still sleeping. Jobs in queue: -22 
Job added 
Still sleeping. Jobs in queue: -21 
Thread 3: I took a job.: 31 
Thread 0: I took a job.: 32 
Thread 5: I took a job.: 33 
Thread 2: I took a job.: 34 
Thread 1: I took a job.: 35 
Thread 7: I took a job.: 36 
Thread 9: I took a job.: 37 
Thread 8: I took a job.: 38 
Thread 6: I took a job.: 39 
Thread 4: I took a job.: 40 
Job added 
Still sleeping. Jobs in queue: -30 
Job added 
Still sleeping. Jobs in queue: -29 
Job added 
Still sleeping. Jobs in queue: -28 
Job added 
Still sleeping. Jobs in queue: -27 
Job added 
Still sleeping. Jobs in queue: -26 
Job added 
Still sleeping. Jobs in queue: -25 
+0

您应该测试'sem_wait'的返回值以确定调用是否实际成功。 – Perception 2012-03-28 01:18:37

+0

你也应该测试'sem_open()'的返回值。 – caf 2012-03-28 02:11:50

回答

3

信号灯坚持死,除非你明确取消它们的链接。您看到的行为是由于线程将旧作业sem_post'd通过先前的进程拖到信号量上造成的。你的sem_getvalue调用会显示这些旧工作的存在,如果这个调用真的起作用了,但它失败了,并且你没有注意到,因为你没有检查sem_getvalue的返回值。 “功能未实现”errno值实际上是从sem_getvalue而不是sem_wait

呼叫之前添加

sem_unlink("semaphore"); 

sem_open和怪异的行为会自行消失。

+0

谢谢!我明白了这一点,但我太新了一个成员,所以我无法回答我自己的问题。 – justindhill 2012-03-29 19:22:39