我是在C中使用pthread库的新手,我为我的班级编写了一个使用它们编写简单程序的作业。该程序的基本描述是需要1个或多个包含网站名称和1个输出文件名的输入文件。然后我需要为每个输入文件创建1个线程来读取网站名称并将它们推送到队列中。然后我需要创建几个线程来从队列中提取这些名称,找到它们的IP地址,然后将该信息写入输出文件。命令行参数如下所示:
./multi-lookup [one or more input files] [single output file name]
我的问题是这个。每当我运行只有一个线程的程序将信息推送到输出文件时,一切正常。当我创建两个线程时,程序挂起,而我的测试都没有" printf"甚至印有报表。我最好的猜测是死锁正在以某种方式发生并且我没有正确使用我的互斥锁,但我无法弄清楚如何修复它。请帮忙!
如果您需要我未提供的任何信息,请告知我们。很抱歉代码中缺少评论。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include "util.h"
#include "queue.h"
#define STRING_SIZE 1025
#define INPUTFS "%1024s"
#define USAGE "<inputFilePath> <outputFilePath>"
#define NUM_RESOLVERS 2
queue q;
pthread_mutex_t locks[2];
int requestors_finished;
void* requestors(void* input_file);
void* resolvers(void* output_file);
int main(int argc, char* argv[])
{
FILE* inputfp = NULL;
FILE* outputfp = NULL;
char errorstr[STRING_SIZE];
pthread_t requestor_threads[argc - 2];
pthread_t resolver_threads[NUM_RESOLVERS];
int return_code;
requestors_finished = 0;
if(queue_init(&q, 10) == QUEUE_FAILURE)
fprintf(stderr, "Error: queue_init failed!\n");
if(argc < 3)
{
fprintf(stderr, "Not enough arguments: %d\n", (argc - 1));
fprintf(stderr, "Usage:\n %s %s\n", argv[0], USAGE);
return 1;
}
pthread_mutex_init(&locks[0], NULL);
pthread_mutex_init(&locks[1], NULL);
int i;
for(i = 0; i < (argc - 2); i++)
{
inputfp = fopen(argv[i+1], "r");
if(!inputfp)
{
sprintf(errorstr, "Error Opening Input File: %s", argv[i]);
perror(errorstr);
break;
}
return_code = pthread_create(&(requestor_threads[i]), NULL, requestors, inputfp);
if(return_code)
{
printf("ERROR: return code from pthread_create() is %d\n", return_code);
exit(1);
}
}
outputfp = fopen(argv[i+1], "w");
if(!outputfp)
{
sprintf(errorstr, "Errord opening Output File: %s", argv[i+1]);
perror(errorstr);
exit(1);
}
for(i = 0; i < NUM_RESOLVERS; i++)
{
return_code = pthread_create(&(resolver_threads[i]), NULL, resolvers, outputfp);
if(return_code)
{
printf("ERROR: return code from pthread_create() is %d\n", return_code);
exit(1);
}
}
for(i = 0; i < (argc - 2); i++)
pthread_join(requestor_threads[i], NULL);
requestors_finished = 1;
for(i = 0; i < NUM_RESOLVERS; i++)
pthread_join(resolver_threads[i], NULL);
pthread_mutex_destroy(&locks[0]);
pthread_mutex_destroy(&locks[1]);
return 0;
}
void* requestors(void* input_file)
{
char* hostname = (char*) malloc(STRING_SIZE);
FILE* input = input_file;
while(fscanf(input, INPUTFS, hostname) > 0)
{
while(queue_is_full(&q))
usleep((rand()%100));
if(!queue_is_full(&q))
{
pthread_mutex_lock(&locks[0]);
if(queue_push(&q, (void*)hostname) == QUEUE_FAILURE)
fprintf(stderr, "Error: queue_push failed on %s\n", hostname);
pthread_mutex_unlock(&locks[0]);
}
hostname = (char*) malloc(STRING_SIZE);
}
printf("%d\n", queue_is_full(&q));
free(hostname);
fclose(input);
pthread_exit(NULL);
}
void* resolvers(void* output_file)
{
char* hostname;
char ipstr[INET6_ADDRSTRLEN];
FILE* output = output_file;
int is_empty = queue_is_empty(&q);
//while(!queue_is_empty(&q) && !requestors_finished)
while((!requestors_finished) || (!is_empty))
{
while(is_empty)
usleep((rand()%100));
pthread_mutex_lock(&locks[0]);
hostname = (char*) queue_pop(&q);
pthread_mutex_unlock(&locks[0]);
if(dnslookup(hostname, ipstr, sizeof(ipstr)) == UTIL_FAILURE)
{
fprintf(stderr, "DNSlookup error: %s\n", hostname);
strncpy(ipstr, "", sizeof(ipstr));
}
pthread_mutex_lock(&locks[1]);
fprintf(output, "%s,%s\n", hostname, ipstr);
pthread_mutex_unlock(&locks[1]);
free(hostname);
is_empty = queue_is_empty(&q);
}
pthread_exit(NULL);
}
答案 0 :(得分:1)
虽然我不熟悉您的"queue.h"
库,但您需要注意以下事项:
当您检查队列是否为空时,您没有获取互斥锁,这意味着可能会发生以下情况:
requestors
线程检查空虚(让我们称之为thread1)并且在它执行pthread_mutex_lock(&locks[0]);
之前(以及if(!queue_is_full(&q))
之后)thread1获得上下文切换requestors
个线程填满队列,如果将尝试插入完整队列,则当thread1最终获取互斥锁时。现在,如果你的队列实现在尝试将更多元素插入已经完整的队列时崩溃,那么thread1永远不会解锁互斥锁,你就会陷入僵局。另一种情况:
resolver
个requestors_finished
个帖子0
最初为(!requestors_finished) || (!is_empty)
,因此is_empty
最初为真。 while(is_empty) usleep((rand()%100));
是真的。pthread_join
并永远睡眠,因为您resolver
此线程您的程序永远不会终止,因为此值永远不会在循环中更新。要记住的一般想法是,当您访问某些非原子资源且可能被其他线程访问的资源时,您需要确保您是唯一一个对此资源执行操作的资源。
使用互斥锁是可以的,但您应该考虑到您无法预测上下文切换何时发生,因此如果您想要检查例如队列是否为空,则应该在锁定互斥锁时执行此操作,直到您解锁它为止完成它,否则无法保证在下一行执行时它会保持空白。
您可能还想考虑详细了解the consumer producer problem。
为了帮助您了解(并控制)使用者(pthread_t requestor_threads[argc - 2];
)线程何时应该运行以及何时生成 producer 线程,您应该考虑使用{{ 3}}。
有些错误。东西:
define
正在使用conditional variables而不是很好 - 如果我没有为您的程序提供任何参数,请考虑会发生什么。决定一些最大值和requestors
或者在检查输入的有效性后动态创建它。可能还有一些问题,但首先要解决这些问题。