即使没有其他人使用线程,线程也无法获取锁

时间:2019-01-05 16:25:17

标签: c multithreading pthreads locking

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

我的代码大多数时候都有效。在某些特定情况下,线程由于某种原因无法获取锁。

我非常努力地调试(如代码所示,使用打印),但是我找不到问题。

#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中。

---编辑---

我发现了问题。我对锁有逻辑上的问题,将其修复。 谢谢大家!

1 个答案:

答案 0 :(得分:0)

  1. 如果filepath = Dequeue()返回非空;那么此循环的下一次迭代将调用Dequeue()而不保持&qlock。如果检查了返回值,您会注意到这一点。

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

  3. 的保护。

如果我正确地阅读了您的代码,则您正在尝试建立一种分派机制,其中名称从scanDir()发出到队列中,然后从队列中拉出,每个队列中都刷新一个新的scanDir()项目。对count的处理是一种尝试,以检测出出队是否由于没有其他事情要做而失败,或者每个人都在忙于执行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有趣的错误检查封面,比所有多余的输入都容易看...

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__)