首页 > 解决方案 > 主线程上的条件变量块

问题描述

我想使用条件变量最多启动 N 个线程来处理一个巨大目录(1M 个文件)的所有文件。

该代码似乎可以工作,但经过一段时间后,它会阻塞在主线程中。在令人沮丧的代码下方:

void* run(void* ctx)
{
    clientCtx* client = (clientCtx*)ctx;
    printf("New file from thread %d: %s\n", client->num, client->filename);
    free(client->filename);
    pthread_mutex_lock(&clientFreeMutex);
    client->state = IDLE_STATE;
    pthread_cond_signal(&clientFreeCond);
    printf("Thread %d is free\n", client->num);
    pthread_mutex_unlock(&clientFreeMutex);
    return NULL;
}

int main(int argc, char** argv)
{

    pthread_t client[MAX_CLIENT] = {0};
    clientCtx ctx[MAX_CLIENT] = {0};
    DIR* directory = NULL;
    struct dirent* element = NULL;

    /* Initialize condition variable for max clients */
    pthread_mutex_init(&clientFreeMutex, NULL);
    pthread_cond_init(&clientFreeCond, NULL);

    /* Initialize contexts for clients */
    for (int cnt = 0; cnt < MAX_CLIENT; cnt ++)
    {
        ctx[cnt].state = IDLE_STATE;
        ctx[cnt].num = cnt;
    }

    directory = opendir(argv[1]);

    while((element = readdir(directory)) != NULL)
    {
        pthread_mutex_lock(&clientFreeMutex);
        int cnt;
        for (cnt = 0; cnt < MAX_CLIENT; cnt++)
        {
            if(ctx[cnt].state == IDLE_STATE)
            {
                ctx[cnt].filename = strdup(element->d_name);
                ctx[cnt].state = BUSY_STATE;
                pthread_create(&client[cnt], NULL, run, &(ctx[cnt]));
                break;
            }
        }
        /* No free client */
        if (cnt == MAX_CLIENT)
        {
            printf("No free thread. Waiting.\n");
            pthread_cond_wait(&clientFreeCond, &clientFreeMutex);
        }
        pthread_mutex_unlock(&clientFreeMutex);
    }
    closedir(directory);
    exit(EXIT_SUCCESS);
}

问题是什么?感谢您的帮助 :)

标签: cmultithreadingrace-condition

解决方案


警告您在单独的线程中使用readdir的值,而对多线程没有任何保护,因此当您(尝试)printf client->file->d_name时,您可能正在同时在主线程中执行readdir修改保存的结果,这有一个未定义行为。

例如,您需要在main中保存strdup并将该字符串保存在clientCtx而不是 中,当然还要在运行中释放它element->file->d_namestruct dirent *

还要注意main末尾缺少一个closedir ,即使在这种情况下这不是一个真正的问题(只需记住您的其他程序)。


推荐阅读