当我在Mac OS X中运行我的代码时,它运行正常。但是在Linux(Ubuntu)上,我得到以下输出:
pmichna@pawel-mac:~/alchemists$ ./alchemists 10
Creating 10 alchemists...
TESTING_ERROR #0
alchemists: malloc.c:2369: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
make: *** [run] Aborted (core dumped)
怎么了? 我的代码:
pmichna@pawel-mac:~/alchemists$ cat alchemists.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM_APPRENTICES 2
pthread_mutex_t *ingredients_mut;
pthread_mutex_t *pots_mut;
pthread_cond_t *ingredients_cond;
pthread_cond_t *pots_cond;
int *ingredients_count;
long num_of_pots;
long num_of_alchemists;
int *pots_usage;
// if the pot was used by the alchemist on the right: 1
// if on the left: -1
// if not yet used: 0
void lock_ingredients(long ingredients_index, long tid)
{
printf("Alchemist #%ld trying to lock ingredients from bowl #%ld...\n", tid, ingredients_index);
pthread_mutex_lock(&ingredients_mut[ingredients_index]);
if(ingredients_count[ingredients_index]>1)
{
ingredients_count[ingredients_index]--;
printf("Alchemist #%ld took an ingredient from bowl #%ld!\n", tid, ingredients_index);
}
else {
printf("Alchemist #%ld found the bowl #%ld empty. Waiting for the filling apprentice...\n", tid, ingredients_index);
pthread_cond_wait(&ingredients_cond[ingredients_index], &ingredients_mut[ingredients_index]);
}
}
void lock_pot(long pot_index, long tid, int expected_pot_value)
{
printf("Alchemist #%ld trying to lock pot #%ld...\n", tid, pot_index);
pthread_mutex_lock(&pots_mut[pot_index]);
if(pots_usage[pot_index] == 0 || pots_usage[pot_index] == expected_pot_value)
{
pots_usage[pot_index] = expected_pot_value;
printf("Alchemist #%ld locked pot #%ld!\n", tid, pot_index);
} else {
printf("Alchemist #%ld found the pot #%ld dirty. :(\n", tid, pot_index);
pthread_cond_wait(&pots_cond[pot_index], &pots_mut[pot_index]);
printf("Alchemist #%ld now can use the cleaned pot #%ld!\n", tid, pot_index);
}
}
void finish_production(int *gold_produced, long tid, long ingredients_index, long pot_index)
{
(*gold_produced)++;
printf("Alchemist #%ld produced gold!. Current status: %d\n", tid, *gold_produced);
pthread_mutex_unlock(&ingredients_mut[ingredients_index]);
printf("Alchemist #%ld released the bowl #%ld.\n", tid, ingredients_index);
pthread_mutex_unlock(&pots_mut[pot_index]);
printf("Alchemist #%ld released the pot #%ld.\n", tid, pot_index);
}
void alchemist_even_work(long tid, int *gold_produced) // has pot on the left; bowl on the right
{
long pot_index = tid/2 - 1;
long ingredients_index = tid/2;
int expected_pot_value = 1;
lock_ingredients(ingredients_index, tid);
lock_pot(pot_index, tid, expected_pot_value);
finish_production(gold_produced, tid, ingredients_index, pot_index);
}
void alchemist_odd_work(long tid, int *gold_produced) //has pot on the right; bowl on the left
{
long pot_index = tid/2;
long ingredients_index = tid/2;
int expected_pot_value = -1;
lock_ingredients(ingredients_index, tid);
lock_pot(pot_index, tid, expected_pot_value);
finish_production(gold_produced, tid, ingredients_index, pot_index);
}
void alchemist_first_work(long tid, int *gold_produced) // has pot on the left; bowl on the right
{
long pot_index = num_of_alchemists/2 - 1;
long ingredients_index = 0;
int expected_pot_value = 1;
lock_ingredients(ingredients_index, tid);
lock_pot(pot_index, tid, expected_pot_value);
finish_production(gold_produced, tid, ingredients_index, pot_index);
}
void *alchemist_work(void *id)
{
long tid = (long)id;
int sleep_time, gold_produced = 0;
printf("Alchemist #%ld started\n", tid);
while(gold_produced < 10)
{
//thinking
srand((int)tid);
sleep_time = rand()%5+1;
printf("Alchemist #%ld going to think %d seconds...\n", tid, sleep_time);
sleep(sleep_time);
printf("Alchemist #%ld is going to try to make gold!\n", tid);
if(tid == 0)
{
alchemist_first_work(tid, &gold_produced);
} else if(tid%2 == 0)
{
alchemist_even_work(tid, &gold_produced);
} else {
alchemist_odd_work(tid, &gold_produced);
}
}
printf("Alchemist #%ld going to lunch.\n", tid);
pthread_exit(NULL);
}
void *apprentice_work(void *id)
{
long tid = (long)id;
long i;
int sleep_time;
switch (tid) {
case 0:
printf("Cleaning apprentice started.\n");
break;
case 1:
printf("Ingredients apprentice started.\n");
break;
}
srand((int)tid);
while (1)
{
sleep_time = rand()%5+1;
switch (tid) {
case 0: // cleaner of pots
printf("Cleaning apprentice going to sleep %d seconds...\n", sleep_time);
sleep(sleep_time);
printf("Cleaning apprentice is now going to clean all the pots\n");
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_lock(&pots_mut[i]);
printf("Apprentice cleaning pot #%ld\n", i);
pots_usage[i] = 0;
pthread_cond_signal(&pots_cond[i]);
pthread_mutex_unlock(&pots_mut[i]);
}
break;
case 1: // ingredients
printf("Ingredients apprentice going to sleep %d seconds...\n", sleep_time);
sleep(sleep_time);
printf("Ingredients apprentice going to fill all the pots...\n");
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_lock(&ingredients_mut[i]);
printf("Apprentice filling bowl #%ld\n", i);
ingredients_count[i] = 5;
pthread_cond_broadcast(&ingredients_cond[i]);
pthread_mutex_unlock(&ingredients_mut[i]);
}
break;
}
}
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
long i;
num_of_alchemists = atol(argv[1]);;
if(num_of_alchemists%2 == 1)
{
fprintf(stderr, "Only even number of alchemists possible!\n");
exit(1);
}
num_of_pots = num_of_alchemists/2; // this variable used alse as number of bowls with ingredients
pthread_t threads_alchemists[num_of_alchemists];
pthread_t threads_apprentices[NUM_APPRENTICES];
pthread_attr_t attr;
ingredients_mut = malloc(num_of_pots*sizeof(pthread_mutex_t));
pots_mut = malloc(num_of_pots*sizeof(pthread_mutex_t));
ingredients_cond = malloc(num_of_pots*sizeof(pthread_cond_t));
ingredients_count = malloc(num_of_pots*sizeof(int));
pots_usage = malloc(num_of_pots*sizeof(int));
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_init(&ingredients_mut[i], NULL);
pthread_mutex_init(&pots_mut[i], NULL);
pthread_cond_init(&ingredients_cond[i], NULL);
pthread_cond_init(&pots_cond[i], NULL);
ingredients_count[i] = 5;
pots_usage[i] = 0;
}
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
printf("Creating %ld alchemists...\n", num_of_alchemists);
for(i = 0; i < num_of_alchemists; i++)
{
printf("TESTING_ERROR #%ld\n", i);
pthread_create(&threads_alchemists[i], &attr, alchemist_work, (void *)i);
}
for(i = 0; i < NUM_APPRENTICES; i++)
{
pthread_create(&threads_apprentices[i], &attr, apprentice_work, (void *)i);
}
/* Wait for all threads to complete */
for (i = 0; i < num_of_alchemists; i++)
{
pthread_join(threads_alchemists[i], NULL);
}
printf ("Main(): Waited and joined with %ld alchemist threads.\n", num_of_alchemists);
// Clean up
pthread_attr_destroy(&attr);
for(i = 0; i < num_of_pots; i++)
{
pthread_mutex_destroy(&ingredients_mut[i]);
pthread_mutex_destroy(&pots_mut[i]);
pthread_cond_destroy(&ingredients_cond[i]);
pthread_cond_destroy(&pots_cond[i]);
}
free(ingredients_mut);
free(ingredients_count);
free(ingredients_cond);
free(pots_mut);
free(pots_usage);
free(pots_cond);
return 0;
}
更新:
所以使用valgrind并运行程序,不时吐出错误。我注意到,他们主要关注条件变量。一个刚刚出现的例子:
pmichna@pawel-mac:~/alchemists$ valgrind --tool=memcheck ./alchemists 4
==3552== Memcheck, a memory error detector
==3552== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3552== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3552== Command: ./alchemists 4
==3552==
==3552== Invalid write of size 4
==3552== at 0x4E3FA32: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:33)
==3552== by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552== Address 0x541a2f8 is 8 bytes after a block of size 80 alloc'd
==3552== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552== by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552==
==3552== Invalid write of size 8
==3552== at 0x4E3FA41: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:40)
==3552== by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552== Address 0x541a2f0 is 0 bytes after a block of size 80 alloc'd
==3552== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552== by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552==
==3552== Invalid write of size 4
==3552== at 0x4E3FA57: pthread_cond_init@@GLIBC_2.3.2 (pthread_cond_init.c:42)
==3552== by 0x4018AA: main (in /home/pmichna/alchemists/alchemists)
==3552== Address 0x541a2fc is 12 bytes after a block of size 80 alloc'd
==3552== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3552== by 0x4017F4: main (in /home/pmichna/alchemists/alchemists)
==3552==
Creating 4 alchemists...
TESTING_ERROR #0
TESTING_ERROR #1
Alchemist #0 started
TESTING_ERROR #2
TESTING_ERROR #3
Alchemist #1 started
Alchemist #1 going to think 4 seconds...
Alchemist #2 started
Alchemist #2 going to think 1 seconds...
Alchemist #3 started
这是否意味着我没有正确使用pthread_cond_init
功能?我该如何解决这个问题?
答案 0 :(得分:4)
您在pthread_cond_init
:
pots_cond
pthread_cond_init(&pots_cond[i], NULL);
但是,分配pots_cond
时使用的大小是pthread_mutex_t
的大小(而不是pthread_cond_t
):
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));
在我的OS X系统上sizeof(pthread_cond_t)
是48,但是sizeof(pthread_mutex_t)
是64,所以这不会导致问题(因为你实际上分配的内存比你需要的多。)
但是,当我启动Ubuntu Linux VM进行检查时,结果sizeof(pthread_mutex_t)
只有40,在这种情况下,你的代码会分配 less 内存,而你会超出pots_cond
。
答案 1 :(得分:3)
pots_cond = malloc(num_of_pots*sizeof(pthread_mutex_t));
您的分配大小为pthread_mutex_t
。但是你需要一个pthread_cond_t
。总是在这张表格上做malloc:
pots_cond = malloc(num_of_pots*sizeof *pots_cond);
这里的评论也在说谎:
/* Wait for all threads to complete */
for (i = 0; i < num_of_alchemists; i++)
{
pthread_join(threads_alchemists[i], NULL);
}
您只需等待threads_alchemists
,而不是所有&threads_apprentices
。当所有炼金术士完成后,你将继续使用学徒线程正在使用的free()变量。
答案 2 :(得分:1)
将整数转换为void*
然后返回作为线程函数的参数从根本上是错误的。如果您使用long
和void*
宽度不同的机器,则最终可能会出现错误的内容。
使用long[something]
表,并将每个线程传递给这样一个表项的地址。