使用两个条件变量&用于在C

时间:2017-09-24 04:29:12

标签: c pthreads signals mutex wait

根据评论者的要求编辑。

该程序创建两个线程。每个线程从两个特定输入文件中的一个读取,每个输入文件包含一个字母或一个&#39; 0&#39; 0每行代码。线程应该将字母读入全局字符数组,然后打印出来。问题是,在达到“0”时,&#39;活动线程必须将控制转移到另一个线程,该线程不应该有一个&#39; 0&#39;在那条线上。 (我们确信,如果文件1在一行上有一个&#39; 0,那么相应行上的文件2就有一个字母。多个零可以互相跟随,多个字母也可以。)< / p>

FILE ONE

h
0
h
0
h
0
h
0
h
0

FILE TWO

0
i
0
i
0
i
0
i
0
i

我正在尝试使用pthread互斥锁定/解锁以及信号并等待使其工作。但是,我一直处于僵局状态。

两个主题。目前,它们彼此镜像意味着它们做同样的事情,只是使用不同的文件和相反的条件。

线程示例:

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 个答案:

答案 0 :(得分:0)

此代码似乎有效。主要的重大变化是在进入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
$

我没有追踪失常。它并不像'第一个线程第一个'那么简单;有一些例子,其中第一个线程首先完成并且完成得很好。

答案 1 :(得分:0)

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