我试图学习如何在c中使用线程,我制作了一个计算文件中行数的程序。该程序为每个文件创建一个线程,以便可以同时计算不同的文件。 我遇到的问题是程序阻塞,这让我觉得发生了死锁。我希望有人可以解释为什么会发生这种情况。测试运行涉及10个文本文件。我也在下面的代码中省略了错误检查。
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
struct thefiles * mailbox;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t flag = PTHREAD_COND_INITIALIZER;
struct thefiles {
char *name;
int linecount;
};
void *count_words(void *a) {
char *buffer;
int fd;
size_t total;
int count = 0;
int size = 0;
struct thefiles *fls = malloc(sizeof(*fls));
fd = open(a, O_RDONLY);
size = lseek(fd, 0, SEEK_END);
buffer = malloc(size);
lseek(fd, 0, SEEK_SET);
read(fd, buffer, size);
while (*buffer) {
if ( *buffer == '\n' )
count++;
buffer++;
}
fls->name = a;
fls->linecount = count;
pthread_mutex_lock(&lock);
if (mailbox != NULL)
pthread_cond_wait(&flag, &lock);
mailbox = fls;
pthread_cond_signal(&flag);
pthread_mutex_unlock(&lock);
}
int main(int argc, char *argv[argc]) {
int i;
int total = 0;
pthread_t *threads;
struct thefiles *fls;
threads = malloc(sizeof(pthread_t)*(--argc));
for (i = 0; i < argc; i++) {
pthread_create(&threads[i], NULL, count_words, argv[i+1]);
}
pthread_mutex_lock(&lock);
while (argc) {
pthread_cond_wait(&flag, &lock);
total += mailbox->linecount;
free(mailbox);
mailbox = NULL;
pthread_cond_signal(&flag);
argc--;
}
printf("%d\n", total);
for (i = 0; i < argc; i++) {
pthread_join(threads[i], NULL);
}
}
答案 0 :(得分:2)
您至少遇到以下问题:
在main()
的循环中,mailbox
返回时,不检查pthread_cond_wait()
是否为NULL。函数pthread_cond_wait()
可以随时返回,即使未调用pthread_cond_signal()
,因此您需要考虑发生这种情况的情况。
同样,在count_words()
中,您不会检查mailbox
是否已变为NULL。应始终在pthread_cond_wait()
循环中调用while
,方法如下:
while (!condition) {
pthread_cond_wait()
}
另外,我建议您检查错误代码 - 即使您只是打印错误并退出,它比盯着没有做任何事情的程序更有教育意义:)
答案 1 :(得分:1)
你无缘无故地复杂化了,你会很难过 当你自己把石头放在路上的时候想出穿线。使用的理由 条件变量用于何时必须同步线程的时间 线程只能继续工作,当另一个已达到某种状态并且两个(或更多线程)必须时 同步。但是,在这种情况下,没有线程依赖于其他线程 在做某事之前,主线程可以愉快地等待所有工人结束 是有用的。
psmears指出了代码中的一些问题。我想继续前进 回答并向您展示如何使用线程来解决此问题 吞吐量太复杂(不需要信号,不需要互斥)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
struct thefiles {
char *name;
int linecount;
int fo_errno;
};
void *count_lines(void *args)
{
struct thefiles *data = args;
FILE *fp = fopen(data->name, "r");
if(fp == NULL)
{
data->linecount = -1; // signaling error
data->fo_errno = errno;
pthread_exit(0);
}
data->linecount = 0;
char line[1024];
char *nl;
while(fgets(line, sizeof line, fp))
{
nl = strchr(line, '\n');
if(nl)
data->linecount++;
}
// last line of file did not end in newline
if(nl == NULL)
data->linecount++;
fclose(fp);
pthread_exit(0);
}
int main(int argc, char **argv)
{
if(argc < 2)
{
fprintf(stderr, "usage: %s file [file ...]\n", argv[0]);
return 1;
}
struct thefiles *fls = calloc(argc - 1, sizeof *fls);
if(fls == NULL)
{
fprintf(stderr, "Not enough memory\n");
return 1;
}
pthread_t *ths = calloc(argc - 1, sizeof *ths);
if(ths == NULL)
{
free(fls);
fprintf(stderr, "Not enough memory\n");
return 1;
}
for(int i = 0; i < argc - 1; ++i)
{
fls[i].name = argv[i+1];
pthread_create(ths + i, NULL, count_lines, fls + i);
}
int total = 0;
for(int i = 0; i < argc - 1; ++i)
{
pthread_join(ths[i], NULL);
if(fls[i].linecount == -1)
{
fprintf(stderr, "%s: %s: %s\n", argv[0], fls[i].name, strerror(fls[i].fo_errno));
continue;
}
printf("%4d %s\n", fls[i].linecount, fls[i].name);
total += fls[i].linecount;
}
printf("%4d total\n", total);
free(ths);
free(fls);
return 0;
}
如你所见,它比你的简单得多,不需要信号和 条件变量等,工人完成一件事:打开 file,count the lines,将结果存储在传递给线程的参数中 并退出。主线程创建线程并连接它们并打印出来 结果:
$ ./b /etc/fstab a.c b.c aa
46 /etc/fstab
124 a.c
98 b.c
./b: aa: No such file or directory
268 total
$ wc -l /etc/fstab a.c b.c aa
46 /etc/fstab
124 a.c
98 b.c
wc: aa: No such file or directory
268 total
当然这只是一个简单的例子,我在此示例的基础上进行了扩展
线程的使用,例如通过线程计算总数
他们自己。在这种情况下,total
必须是共享资源,您需要一个
mutex保护它,首先尝试实现它。