线程池 - 当有多个任务而不是线程时处理一个案例

时间:2015-06-15 20:57:09

标签: c multithreading pthreads

我刚刚进入多线程编程,并尝试使用pthreads实现一个简单的线程池。

我试图使用条件变量来发信号通知工作线程,队列中有作业等待。但由于某种原因,我无法弄清楚机制是不起作用。

Bellow是相关的代码段:

typedef struct thread_pool_task
{
    void (*computeFunc)(void *);
    void *param;
} ThreadPoolTask;

typedef enum thread_pool_state
{
    RUNNING = 0,
    SOFT_SHUTDOWN = 1,
    HARD_SHUTDOWN = 2
} ThreadPoolState;

typedef struct thread_pool
{
    ThreadPoolState poolState;
    unsigned int poolSize;
    unsigned int queueSize;
    OSQueue* poolQueue;
    pthread_t* threads;
    pthread_mutex_t q_mtx;
    pthread_cond_t q_cnd;
} ThreadPool;
static void* threadPoolThread(void* threadPool){

    ThreadPool* pool = (ThreadPool*)(threadPool);
    for(;;) 
    {
        /* Lock must be taken to wait on conditional variable */
        pthread_mutex_lock(&(pool->q_mtx));

        /* Wait on condition variable, check for spurious wakeups.
           When returning from pthread_cond_wait(), we own the lock. */
        while( (pool->queueSize == 0) && (pool->poolState == RUNNING) ) 
        {
            pthread_cond_wait(&(pool->q_cnd), &(pool->q_mtx));
        }

        printf("Queue size: %d\n", pool->queueSize);

        /* --- */
        if (pool->poolState != RUNNING){
            break;
        }

        /* Grab our task */
        ThreadPoolTask* task = osDequeue(pool->poolQueue);
        pool->queueSize--;


        /* Unlock */
        pthread_mutex_unlock(&(pool->q_mtx));

        /* Get to work */
        (*(task->computeFunc))(task->param);
        free(task);
    }


    pthread_mutex_unlock(&(pool->q_mtx));
    pthread_exit(NULL);
    return(NULL);
}
ThreadPool* tpCreate(int numOfThreads)
{
    ThreadPool* threadPool = malloc(sizeof(ThreadPool));
    if(threadPool == NULL) return NULL;


    /* Initialize */
    threadPool->poolState = RUNNING;
    threadPool->poolSize = numOfThreads;
    threadPool->queueSize = 0;

    /* Allocate OSQueue and threads */
    threadPool->poolQueue = osCreateQueue();
    if (threadPool->poolQueue == NULL)
    {

    }
    threadPool->threads = malloc(sizeof(pthread_t) * numOfThreads);
    if (threadPool->threads == NULL)
    {

    }

    /* Initialize mutex and conditional variable */
    pthread_mutex_init(&(threadPool->q_mtx), NULL);
    pthread_cond_init(&(threadPool->q_cnd), NULL);

    /* Start worker threads */
    for(int i = 0; i < threadPool->poolSize; i++) 
    {
        pthread_create(&(threadPool->threads[i]), NULL, threadPoolThread, threadPool);
    }

    return threadPool;
}
int tpInsertTask(ThreadPool* threadPool, void (*computeFunc) (void *), void* param)
{
    if(threadPool == NULL || computeFunc == NULL) {
        return -1;
    }

    /* Check state and create ThreadPoolTask */
    if (threadPool->poolState != RUNNING) return -1;
    ThreadPoolTask* newTask = malloc(sizeof(ThreadPoolTask));
    if (newTask == NULL) return -1;
    newTask->computeFunc = computeFunc;
    newTask->param = param;

    /* Add task to queue */
    pthread_mutex_lock(&(threadPool->q_mtx));
    osEnqueue(threadPool->poolQueue, newTask);
    threadPool->queueSize++;
    pthread_cond_signal(&(threadPool->q_cnd));
    pthread_mutex_unlock(&threadPool->q_mtx);

    return 0;
}

问题在于,当我创建一个包含1个线程的池并为其添加大量作业时,它不会执行所有作业。

[编辑:] 我尝试运行以下代码来测试基本功能:

void hello (void* a)
{
   int i = *((int*)a);
   printf("hello: %d\n", i);
}
void test_thread_pool_sanity()
{
   int i;

   ThreadPool* tp = tpCreate(1);

   for(i=0; i<10; ++i)
   {
      tpInsertTask(tp,hello,(void*)(&i));
   }
}

我希望输入如下:

hello: 0
hello: 1
hello: 2
hello: 3
hello: 4
hello: 5
hello: 6
hello: 7
hello: 8
hello: 9

相反,有时我得到以下输出:

Queue size: 9 //printf added for debugging within threadPoolThread
hello: 9
Queue size: 9 //printf added for debugging within threadPoolThread
hello: 0

有时我根本得不到任何输出。 我错过了什么?

1 个答案:

答案 0 :(得分:1)

当你拨打tpInsertTask(tp,hello,(void*)(&i));时,你正在传递堆叠中的i的地址。这有很多问题:

  1. 每个帖子都获得相同的地址。我猜测hello函数接受该地址并打印出* param,它们都指向堆栈上的相同位置。
  2. 由于我在堆栈上,一旦test_thread_pool_sanity返回,最后一个值就会丢失,并且会被其他代码覆盖,因此该值未定义。
  3. 根据那时工作线程完成任务,而当主测试线程安排任务时,您将获得不同的结果。

    您需要将传递的参数保存为任务的一部分,以确保每个任务都是唯一的。

    编辑:你还应该检查pthread_create的返回码,看它是否失败。