代码之家  ›  专栏  ›  技术社区  ›  Don

在C语言中使用条件变量和互斥体来同步线程

  •  0
  • Don  · 技术社区  · 7 年前

    文件一

    h
    0
    h
    0
    h
    0
    h
    0
    h
    0
    

    文件二

    0
    i
    0
    i
    0
    i
    0
    i
    0
    i
    

    我正在尝试使用pthread mutex lock/unlock以及signal,并等待这项工作。然而,我一直处于僵局状态。

    螺纹。目前,它们相互镜像意味着它们做相同的事情,只是使用不同的文件和相反的条件。

    线程示例:

    char final[1001];
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER;
    pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER;
    
    int w = 1;
    
    void *get()
    {
    //start reading
    while (count < //number)
    {
        pthread_mutex_lock(&lock);
    
        //read line
    
        //if we've reached a zero
        {
            w = 2;             
    
             while(w == 2)
             {
                pthread_cond_wait(&condition1, &lock);
             }
    
    
             pthread_mutex_unlock(&lock);
        }
        else
        {   
           if(w == 1)
           {
    
                if(strlen(placeHolderChars)>0)
                {
                     placeHolderChars[1] = '\0';
                }
    
                //copy char to array
    
                w= 2;
                pthread_cond_signal(&condition2);
                pthread_mutex_unlock(&lock);
            }
        }
    
        if(feof(file))
        {     
            fclose(file);
            break;
        }
        count++;
    
     }
    
    return 0;
    }
    

    更新:在使用较大的文件时,先信号后等待策略实际上不起作用。还在努力!

    2 回复  |  直到 7 年前
        1
  •  0
  •   Jonathan Leffler    7 年前

    此代码似乎有效。主要的重大变化是增加 pthread_cond_signal() 在进入 while (who == N) 循环。

    其他更改包括基本的调试打印,以便于查看哪个线程正在做什么。请注意,调试消息以换行结束。

    #include <assert.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    extern void *getMessage1(void *arg);
    extern void *getMessage2(void *arg);
    
    static char message[4096];
    
    int main(void)
    {
        pthread_t id1;
        pthread_t id2;
    
        pthread_create((&id1), NULL, getMessage1, NULL);
        pthread_create((&id2), NULL, getMessage2, NULL);
    
        pthread_join(id1, NULL);
        pthread_join(id2, NULL);
    
        for (int j = 0; j < 1001 && message[j] != '\0'; j++)
            printf("%c ", message[j]);
        putchar('\n');
    
        return 0;
    }
    
    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    static pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER;
    static pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER;
    
    static int who = 1;
    
    void *getMessage1(void *arg)
    {
        assert(arg == NULL);
        const char filename[] = "Student1";
        FILE *studentOne = fopen(filename, "r");
        if (studentOne == NULL)
        {
            fprintf(stderr, "Failed to open file %s for reading\n", filename);
            exit(EXIT_FAILURE);
        }
    
        size_t howManyChars;
        char *placeHolderChars;
        int count = 1;
    
        while (count < 501)
        {
            placeHolderChars = NULL;
            if (getline(&placeHolderChars, &howManyChars, studentOne) == -1)
                break;
            printf("M1(%d): [%s]\n", count, placeHolderChars);
            pthread_mutex_lock(&lock);
            if (strcmp(placeHolderChars, "0\n") == 0)
            {
                printf("M1: Two's turn - 1\n");
                pthread_cond_signal(&condition2);
                who = 2;
                while (who == 2)
                {
                    pthread_cond_wait(&condition1, &lock);
                }
                free(placeHolderChars);
            }
            else
            {
                if (who == 1)
                {
                    if (strlen(placeHolderChars) > 0)
                    {
                        placeHolderChars[1] = '\0';
                    }
                    strcat(message, placeHolderChars);
                    free(placeHolderChars);
                    who = 2;
                    pthread_cond_signal(&condition2);
                }
                else
                    printf("M1: Two's turn - 2\n");
            }
            pthread_mutex_unlock(&lock);
            count++;
        }
    
        fclose(studentOne);
        return 0;
    }
    
    void *getMessage2(void *arg)
    {
        assert(arg == NULL);
        const char filename[] = "Student2";
        FILE *studentTwo = fopen(filename, "r");
        if (studentTwo == NULL)
        {
            fprintf(stderr, "Failed to open file %s for reading\n", filename);
            exit(EXIT_FAILURE);
        }
    
        size_t howManyChars;
        char *placeHolderChars;
        int count = 0;
    
        while (count < 501)
        {
            placeHolderChars = NULL;
            if (getline(&placeHolderChars, &howManyChars, studentTwo) == -1)
                break;
            printf("M2(%d): [%s]\n", count, placeHolderChars);
            pthread_mutex_lock(&lock);
            if (strcmp(placeHolderChars, "0\n") == 0)
            {
                printf("M2: One's turn - 1\n");
                pthread_cond_signal(&condition1);
                who = 1;
                while (who == 1)
                {
                    pthread_cond_wait(&condition2, &lock);
                }
                free(placeHolderChars);
            }
            else
            {
                if (who == 2)
                {
                    if (strlen(placeHolderChars) > 0)
                    {
                        placeHolderChars[1] = '\0';
                    }
                    strcat(message, placeHolderChars);
                    free(placeHolderChars);
                    who = 1;
                    pthread_cond_signal(&condition1);
                }
                else
                    printf("M2: One's turn - 2\n");
            }
            pthread_mutex_unlock(&lock);
            count++;
        }
    
        fclose(studentTwo);
        return 0;
    }
    

    您应该能够优化代码,以便将包含相关每线程数据(文件名、当前线程条件、其他线程条件,可能是“线程ID”)的结构传递给单个函数,因此您只需要 getMessage()

    输出:

    M1(1): [h
    ]
    M2(0): [0
    ]
    M1(2): [0
    ]
    M2: One's turn - 1
    M1: Two's turn - 1
    M2(1): [i
    ]
    M2(2): [0
    ]
    M2: One's turn - 1
    M1(3): [h
    ]
    M1(4): [0
    ]
    M1: Two's turn - 1
    M2(3): [i
    ]
    M2(4): [0
    ]
    M2: One's turn - 1
    M1(5): [h
    ]
    M1(6): [0
    ]
    M1: Two's turn - 1
    M2(5): [i
    ]
    M2(6): [0
    ]
    M2: One's turn - 1
    M1(7): [h
    ]
    M1(8): [0
    ]
    M1: Two's turn - 1
    M2(7): [i
    ]
    M2(8): [0
    ]
    M2: One's turn - 1
    M1(9): [h
    ]
    M1(10): [0
    ]
    M1: Two's turn - 1
    M2(9): [i
    ]
    h i h i h i h i h i 
    

    我对这段代码不太满意。正如我所暗示的那样,我创建了一个修改版本,其中包含两个线程使用的单个函数,并修改了读取的行的打印,以避免打印换行符(使输出更加紧凑)。有时并非所有时候,它都会在最后陷入僵局。两个样本记录道,一个工作,一个死锁(程序名 pth47

    $ pth47
    M2(1): [0]
    M2: 1's turn - 1
    M1(1): [h]
    M1(2): [0]
    M1: 2's turn - 1
    M2(2): [i]
    M2(3): [0]
    M2: 1's turn - 1
    M1(3): [h]
    M1(4): [0]
    M1: 2's turn - 1
    M2(4): [i]
    M2(5): [0]
    M2: 1's turn - 1
    M1(5): [h]
    M1(6): [0]
    M1: 2's turn - 1
    M2(6): [i]
    M2(7): [0]
    M2: 1's turn - 1
    M1(7): [h]
    M1(8): [0]
    M1: 2's turn - 1
    M2(8): [i]
    M2(9): [0]
    M2: 1's turn - 1
    M1(9): [h]
    M1(10): [0]
    M1: 2's turn - 1
    M2(10): [i]
    h i h i h i h i h i 
    $ pth47
    M1(1): [h]
    M2(1): [0]
    M1(2): [0]
    M2: 1's turn - 1
    M1: 2's turn - 1
    M2(2): [i]
    M2(3): [0]
    M2: 1's turn - 1
    M1(3): [h]
    M1(4): [0]
    M1: 2's turn - 1
    M2(4): [i]
    M2(5): [0]
    M2: 1's turn - 1
    M1(5): [h]
    M1(6): [0]
    M1: 2's turn - 1
    M2(6): [i]
    M1(7): [h]
    M1(8): [0]
    M2(7): [0]
    M1: 2's turn - 1
    M2: 1's turn - 1
    M1(9): [h]
    M1(10): [0]
    M2(8): [i]
    M1: 2's turn - 1
    M2(9): [0]
    M2: 1's turn - 1
    ^C
    $
    

    我还没有找到异常现象。这并不像“线程一个先走”那么简单;有一些例子是线程一先完成的,它完成得很好。

        2
  •  0
  •   Erik Alapää    7 年前

    我认为提出这个问题的人做了两次,有点恼人。FWIW,这是我对副本的回答: Pthread Synchronization: Back & Forth Reading of Two Text Files