C线程池 - 传递参数重复

时间:2015-01-23 00:26:00

标签: c threadpool

拥有以下源代码:

#define THREAD 32
#define QUEUE 300
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include "threadpool.h"


struct fparam {
int no;
};

int tasks = 0, done = 0;
pthread_mutex_t lock;

int exit_me(){
    pthread_mutex_lock(&lock);
    tasks--;
    pthread_mutex_unlock(&lock);
    return 0;
}

void dummy_task(void *arg) {
    struct fparam *args = arg;
    pthread_mutex_lock(&lock);
    done++;
    pthread_mutex_unlock(&lock);
    printf("Thread INDEX: %d started.\n",args->no);
    exit_me();
}

int main()
{
    int t, result;
    threadpool_t *pool;
    struct fparam push_args;
    pthread_mutex_init(&lock, NULL);
    pool = threadpool_create(THREAD, QUEUE, 0);
    fprintf(stderr, "Pool started with %d threads and "
            "queue size of %d\n", THREAD, QUEUE);

    for (t = 0;t < 2000; t++){
        push_args.no = t;
        result = threadpool_add(pool, &dummy_task, (void *)&push_args, 0);
        if (result == 0){
            pthread_mutex_lock(&lock);
            tasks++;
            pthread_mutex_unlock(&lock);
        } else {
             printf("Something went wrong with thread: %d\n", t);
        }
        while(tasks  >= QUEUE); // do nothing until tasks running is less than max queue.
    }

    while(tasks >= 1);
    return 0;
}

我正在使用https://github.com/mbrossard/threadpool池实现。

一切看起来都不错,但在检查传递给虚拟函数的t参数时,我可以看到重复:

Thread INDEX: 1998 started.
Thread INDEX: 1999 started.
Thread INDEX: 1999 started.
Thread INDEX: 1974 started.
Thread INDEX: 1979 started.
Thread INDEX: 1979 started.
Thread INDEX: 1978 started.
Thread INDEX: 1979 started.
Thread INDEX: 1979 started.

我认为代码中没有任何竞争条件,因为在函数内声明了fparam结构。 有什么想法或建议吗?

2 个答案:

答案 0 :(得分:2)

是的,您的push_args正在遭遇竞争状况。虽然每个线程都获得了您传入的参数的副本,但您执行该操作的方式意味着每个线程都会获得相同的值(指针)。

主线程不断修改背后的指针,而不是启动新线程,但线程本身正在启动并使用它。

以下列顺序为例:

  • main将ID设置为1并创建线程A.
  • main将ID设置为2并创建线程B.
  • 线程A启动并读取ID为2。
  • 线程B启动并读取ID为2。

现在两个线程都使用ID为2。

如果你想这样做,你可能需要等到每个线程都制作了这个ID值的本地副本,然后再在main中进行修改。

另一种方式是&#34;自动&#34;通过将它作为参数本身(而不是指向它的指针)传递给线程一个本地副本,如:

result = threadpool_add(pool, &dummy_task, (void *)t, 0);

这可确保每个线程都收到t的本地化副本,就像调用时一样。您只需将其从void*投回int

如果你不能使用一个能够与指针进行强制转换的简单变量,并且你不想等到每个线程在开始下一个之前制作了本地副本,你需要分开传递的物品。

实现此目的的一种方法是拥有项目的数组(在您的情况下为结构)并将它们传递给等效的线程。例如,您可以执行以下操作:

static struct payload items[100];
for (int i = 0; i < 100; i++) {
    items[i].t = i;
    result = threadpool_add(pool, &dummy_task, (void *)(&items[i]), 0);
    // check result.
}

这在内存上有点贵,但它解决了竞争条件的问题而无需序列化线程创建。

答案 1 :(得分:0)

我实际上是成功的。请检查以下内容,并建议是否有其他错误。

int main()
{
    int t, result;
    threadpool_t *pool;
    struct fparam *push_args = NULL;

    pthread_mutex_init(&lock, NULL);
    pool = threadpool_create(THREAD, QUEUE, 0);
    fprintf(stderr, "Pool started with %d threads and "
                    "queue size of %d\n", THREAD, QUEUE);
    for (t = 0;t < 2000; t++){
            push_args = (struct fparam*)malloc(sizeof *push_args);
            push_args->no = t;
            result = threadpool_add(pool, &dummy_task, push_args, 0);
            if (result == 0){
                    pthread_mutex_lock(&lock);
                    tasks++;
                    pthread_mutex_unlock(&lock);
            } else {
                    printf("Something went wrong with thread: %d\n", t);
            }
            while(tasks  >= QUEUE); // do nothing until tasks running is less than max queue.
    }

    while(tasks >= 1);
    free(push_args);
    return 0;
}