【问题标题】:Threads are not able to acquire locks even though no one else is using them线程无法获取锁,即使没有其他人使用它们
【发布时间】:2019-05-31 21:47:01
【问题描述】:

我正在尝试编写一个使用线程的代码,以查找名称中包含特定字符串的文件。

我的代码在大多数情况下都能正常工作。在某些特定情况下,线程由于某种原因无法获取锁。

我非常努力地调试(使用打印,正如您在代码中看到的那样)但我找不到问题。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>

void* threadFunction(void* searchTerm);
bool scanDirName(char * path, char * searchTerm);
int numOfThreads;
pthread_mutex_t qlock;
pthread_cond_t cond;
int count;
int matchingFiles;

struct Node {
    char* data;
    struct Node* next;
};

// Two global variables to store an address of front and rear nodes.
    struct Node* front = NULL;
    struct Node* rear = NULL;

// To Enqueue an integer
void Enqueue(char* x) {
/*    printf("\nhere\n");*/
    struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
    temp->data =x;
    temp->next = NULL;
    if(front == NULL && rear == NULL){
        front = rear = temp;
        pthread_cond_signal(&cond);
        return;
    }
    rear->next = temp;
    rear = temp;

}

// To Dequeue an integer.
char* Dequeue() {
    struct Node* temp = front;
    if(front == NULL) {
        return NULL;
    }
    char* data;
    data = front->data;
    if(front == rear) {
        front = rear = NULL;

    }
    else {
        front = front->next;
    }
//    printf("\nfreeing %p, %s\n", temp, temp->data);
    free(temp);
    return data;
}

void* threadFunction(void* st) {
    bool isFinished;
    isFinished = false;
    pthread_mutex_lock(&qlock);
    while (true) {
        char *filepath;
        char *searchTerm;
        searchTerm = (char *) st;

        filepath = Dequeue();
        pthread_mutex_unlock(&qlock);
        if (filepath == NULL) {
            printf("%ld waiting for lock \n",(long) pthread_self());
            pthread_mutex_lock(&qlock);
            count++;
            if (isFinished) {
                printf("%ld waking u up, we found %d items!\n",(long) pthread_self(), matchingFiles);
                pthread_cond_broadcast(&cond);
                if (count == numOfThreads) {
                    printf("Thread exited: %ld\n", (long) pthread_self());
                    pthread_mutex_unlock(&qlock);
                    pthread_exit((void*)0);
                }
            }
            isFinished = false;

            printf("%ld going to sleep\n",(long) pthread_self());
            pthread_cond_wait(&cond, &qlock);
            printf("%ld Woke up, try to compare %d == %d\n",(long) pthread_self(), count, numOfThreads);
            if (count == numOfThreads) {
                printf("Thread exited: %ld\n", (long) pthread_self());
                pthread_mutex_unlock(&qlock);
                pthread_exit((void*)1);
            }
            printf("%ld compare failed \n",(long) pthread_self());
            count--;

        }
        else {
            printf("%ld deq 1 item \n",(long) pthread_self());
            isFinished = scanDirName(filepath, searchTerm);
        }
    }



}

bool scanDirName(char * path, char * searchTerm){
    DIR * d = opendir(path); // open the path
    char* str3;

    if(d==NULL) return false; // if was not able return

;

    struct dirent * dir; // for the directory entries
    while ((dir = readdir(d)) != NULL) // if we were able to read somehting from the directory
    {
        printf("%ld STARTED A ROUND!\n",(long) pthread_self());
        if(dir-> d_type == DT_DIR){ //
            if (dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 & strcmp(dir->d_name, "..") != 0) // if it is a directory
            {
                str3 = malloc((1+strlen("/")+ strlen(path)+ strlen(dir->d_name))*sizeof(char));
                if (!str3){
                    return false;
                }

                strcpy(str3, path);
                strcat(str3, "/");
                strcat(str3, dir->d_name);
//                printf("\n---\n%s\n---\n",str3);
                printf("%ld waiting for lock in func \n",(long) pthread_self());
                pthread_mutex_lock(&qlock);
                printf("%ld wake threads \n",(long) pthread_self());
                Enqueue(str3);
                pthread_cond_signal(&cond);
                printf("%ld enq \n",(long) pthread_self());
                pthread_mutex_unlock(&qlock);
                printf("%ld locks gone \n",(long) pthread_self());

            }
        }
        else if(dir-> d_type == DT_REG){ //
            if(strstr(dir->d_name, searchTerm)){
                matchingFiles++;
                /*printf("%s/%s\n", path, dir->d_name);*/
            }
        }

        printf("%ld finished A ROUND!\n",(long) pthread_self());
    }
    printf("%ld finished scanning!\n",(long) pthread_self());
    fflush(stdout);
    closedir(d); // finally close the directory
    if (count == numOfThreads-1) {
        return true;
    }
    return false;
}

int main(int argc, char* argv[]){
    count = 0;
    pthread_mutex_init(&qlock, NULL);
    pthread_cond_init(&cond, NULL);
    matchingFiles = 0;

    if (argc != 4){
        printf("ERROR\n");
        exit(1);
    }
    char* rootSearchDir = argv[1];
    char* searchTerm = argv[2];
    int threadsNumber = atoi(argv[3]);

    pthread_t threadsCollection[threadsNumber];

    Enqueue(rootSearchDir);
    numOfThreads = threadsNumber;

    int i;
    for (i=0; i<threadsNumber; i++){
        if(pthread_create(&threadsCollection[i], NULL, threadFunction, (void*)searchTerm)) {

            fprintf(stderr, "Error creating thread\n");
            pthread_mutex_destroy(&qlock);
            return 1;

        }
    }

    int rc;

    for (i=0; i<threadsNumber; i++){
        rc = pthread_join((threadsCollection[i]), NULL);
        if(rc) {
            fprintf(stderr, "Error joining thread, %d\n", rc);
            pthread_mutex_destroy(&qlock);
            return 1;

        }
    }



}

我已附上所有代码(可能很重要),但我怀疑问题出在 scanDirName 或 threadFunction 中。

--- 编辑 ---

我发现了问题。我的锁有逻辑问题,修复它。 谢谢大家!

【问题讨论】:

  • isFinished = false; 之后和if (count == numOfThreads) 之前对pthread_cond_wait 的调用中,线程到底在等待什么?如果它正在等待对Enqueue 的呼叫,那么您怎么知道这还没有发生而您正在等待已经发生的事情?
  • @DavidSchwartz 线程正在等待,直到有人向此条件变量发送信号\广播。我这样做是因为我不想忙于等待并浪费 CPU 时间。
  • 使用 gdb 和 thr a a bt 进行调试可能会产生比 printf 更好的结果。
  • @DarkSkylo 你不能那样使用条件变量。它们是无状态的,所以你不能等待它们改变状态。迟早,这会咬你。

标签: c multithreading pthreads locking


【解决方案1】:
  1. 如果 filepath = Dequeue() 返回非空值;那么这个循环的下一次迭代将调用 Dequeue() 而不持有 &qlock。如果您检查返回值,您会注意到这一点。

  2. scanDirName 依赖于 count 来设置它的返回值;但是它不受 qlock { 参见 #1 }

  3. 的保护

如果我正确阅读了您的代码,则您正在尝试创建一种调度机制,其中名称从 scanDir() 发出到队列中,然后从队列中拉出,其中为每个新的 scanDir() 旋转物品。对count的处理是试图检测出Dequeue失败是因为无事可做,还是大家都在忙着做scanDirs。

有一种更简单的机制:使用两个队列。 Scandir 将条目转储到“NameQ”中,线程函数从 NameQ 中提取名称,将它们转储到 WorkQ。当一个线程用完 NameQ 项目时,它会在 WorkQ 中寻找新的要调度的项目。然后你只需要跟踪scanDir() 中有多少线程。如果两个队列都是空的,并且 scanDir 中的线程数为零,则没有什么可做的。您的基本逻辑最终看起来像这样:

void* threadFunction(void* st) {
    static int nreader = 0;
    int ncount = 0, wcount = 0;
    char *searchTerm;
    searchTerm = (char *) st;
    Menter(&qlock);
    while (nreader || !(empty(&Names) && empty(&Work))) {
        char *filepath;
        filepath = Dequeue(&Names);
        if (filepath) {
            ncount++;
            Enqueue(&Work, filepath);
        } else if ((filepath = Dequeue(&Work)) != NULL) {
            wcount++;
            nreader++;
            Mexit(&qlock);
            scanDirName(filepath, searchTerm);
            Menter(&qlock);
            nreader--;
            cv_wake(&cond);
        } else {
            cv_wait(&cond, &qlock);
        }
    }
    Mexit(&qlock);
    printf("%p: %d, %d items\n", pthread_self(), ncount, wcount);
    return NULL;
}

Mexit、Menter 是 posix funs 的错误检查封面,比所有多余的打字更容易让人眼前一亮......

static int menter(pthread_mutex_t *m, int line) {
    if (pthread_mutex_lock(m) != 0) {
        fprintf(stderr, "%p:%d  Mutex lock failed!\n", pthread_self(),line);
        abort();
    }
    DEBUG_LOCK("%d locked\n", line);
    return 0;
}
#define Menter(x) menter((x), __LINE__)
static int mexit(pthread_mutex_t *m, int line) {
    DEBUG_LOCK("%d unlocked\n", line);
    if (pthread_mutex_unlock(m) != 0) {
        fprintf(stderr, "%p:%d  Mutex unlock failed!\n", pthread_self(),line);
        abort();
    }
    return 0;
}
#define Mexit(x) mexit((x), __LINE__)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 2017-03-08
    • 2020-06-16
    • 2018-10-19
    相关资源
    最近更新 更多