下面的代码摘录一般会导致任何问题吗?
void * fct(void * p)
{
int i = 0;
int * input = (int *) p;
int * arr = malloc((*input)*sizeof(int));
/* put something in arr */
return (void *) arr
}
int main()
{
/* prologue */
for (i = 0; i < MAX_THREADS; i++)
{
rc = pthread_create(&threads[i], NULL, fct, (void *) &input[i]);
}
/* epilogue */
}
我实际上不理解并没有找到答案是:线程例程(i
, input
, arr
)内的变量共享吗?我理解pthreads的方式是fct
的不同副本被称为MAX_THREADS
次,每个副本都有自己的一组变量。
全局变量在线程之间共享。作为参数传递的输入的引用也是如此。
另一方面,您引用的变量是局部变量,它们在堆栈上分配并且彼此不同。这与线程无关,而是与堆栈指针的当前值有关。malloc的结果也会有所不同,因为malloc会被调用多次。所有这些都与线程的关系非常松散,并且在同一个线程中对函数的几个调用将表现出相同的内存(而不是时间)。在局部变量方面会有细微的区别,因为堆栈不是在线程之间共享的,仅此而已。
对于您的问题"是否…代码摘录一般会导致任何问题"的简洁回答是"否"。正如在各种评论和回答中所指出的:
函数中的局部自动变量本质上是线程安全的;无论在线程上下文中、递归上下文中还是对函数的简单调用中,每次调用函数都会重新分配它们。全局变量通常是共享的——除非创建为线程本地存储(TLS)或线程特定存储(TSS——C11中使用的术语)。代码[在问题]应该是好的,除了内存泄漏时,线程返回-但线程清理代码没有显示,所以这可能是不公平的。您需要通过pthread_join()
捕获返回值并释放它以避免泄漏。
将代码摘录充实为工作代码,使用互斥锁来显示并确保一次一个线程正在打印其数据,您可以编写如下代码:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void *fct(void *p)
{
int number = *(int *)p;
int *arr = malloc(number * sizeof(int)); // Error check missing!
for (int i = 0; i < number; i++)
arr[i] = number + (7 * i + 5) % 11;
pthread_mutex_lock(&mtx); // Error check missing!
printf("Integers %2d (%p):", number, (void *)arr);
for (int i = 0; i < number; i++)
printf(" %3d", arr[i]);
putchar('n');
pthread_mutex_unlock(&mtx); // Error check missing!
return (void *)arr;
}
enum { MAX_THREADS = 10 };
int main(void)
{
pthread_t threads[MAX_THREADS];
int input[MAX_THREADS];
int *result[MAX_THREADS];
/* Initialization */
for (int i = 0; i < MAX_THREADS; i++)
input[i] = (3 * i + 2) % 13 + 1;
/* Thread creation */
for (int i = 0; i < MAX_THREADS; i++)
{
pthread_mutex_lock(&mtx); // Error check missing!
printf("Launch %d: %2dn", i, input[i]);
pthread_mutex_unlock(&mtx); // Error check missing!
int rc = pthread_create(&threads[i], NULL, fct, (void *)&input[i]);
if (rc != 0)
{
fprintf(stderr, "Oops creating thread %dn", i);
exit(EXIT_FAILURE);
}
}
/* Thread harvesting */
for (int i = MAX_THREADS; i-- > 0; )
{
void *vp;
int rc = pthread_join(threads[i], &vp);
if (rc != 0)
{
fprintf(stderr, "Oops joining thread %dn", i);
exit(EXIT_FAILURE);
}
printf("Thread %d: %2d (%p)n", i, input[i], vp);
result[i] = vp;
}
/* Finalization */
for (int i = 0; i < MAX_THREADS; i++)
{
printf("Master - %d:", i);
for (int j = 0; j < input[i]; j++)
printf(" %3d", result[i][j]);
putchar('n');
free(result[i]);
}
pthread_mutex_destroy(&mtx);
return 0;
}
运行示例Launch 0: 3
Launch 1: 6
Launch 2: 9
Launch 3: 12
Integers 6 (0x7faf9ae01890): 11 7 14 10 6 13
Integers 3 (0x7faf9af00000): 8 4 11
Integers 9 (0x7faf9b800000): 14 10 17 13 9 16 12 19 15
Launch 4: 2
Integers 12 (0x7faf9b800030): 17 13 20 16 12 19 15 22 18 14 21 17
Launch 5: 5
Integers 2 (0x7faf9af00010): 7 3
Integers 5 (0x7faf9b800060): 10 6 13 9 5
Launch 6: 8
Launch 7: 11
Integers 8 (0x7faf9ae018b0): 13 9 16 12 8 15 11 18
Launch 8: 1
Integers 11 (0x7faf9b800080): 16 12 19 15 11 18 14 21 17 13 20
Launch 9: 4
Integers 1 (0x7faf9af00020): 6
Integers 4 (0x7faf9b8000b0): 9 5 12 8
Thread 9: 4 (0x7faf9b8000b0)
Thread 8: 1 (0x7faf9af00020)
Thread 7: 11 (0x7faf9b800080)
Thread 6: 8 (0x7faf9ae018b0)
Thread 5: 5 (0x7faf9b800060)
Thread 4: 2 (0x7faf9af00010)
Thread 3: 12 (0x7faf9b800030)
Thread 2: 9 (0x7faf9b800000)
Thread 1: 6 (0x7faf9ae01890)
Thread 0: 3 (0x7faf9af00000)
Master - 0: 8 4 11
Master - 1: 11 7 14 10 6 13
Master - 2: 14 10 17 13 9 16 12 19 15
Master - 3: 17 13 20 16 12 19 15 22 18 14 21 17
Master - 4: 7 3
Master - 5: 10 6 13 9 5
Master - 6: 13 9 16 12 8 15 11 18
Master - 7: 16 12 19 15 11 18 14 21 17 13 20
Master - 8: 6
Master - 9: 9 5 12 8
充分披露:
在Mac OS X 10.11.6上测试,GCC 6.1.0和Valgrind 3.12.0.SVN。当在Valgrind下运行时,它会崩溃。当不在Valgrind下运行时不会崩溃。
==3412== Memcheck, a memory error detector
==3412== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3412== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3412== Command: ./pth37
==3412==
Launch 0: 3
Launch 1: 6
Launch 2: 9
Launch 3: 12
Launch 4: 2
Integers 3 (0x100aaf5b0): 8 4 11
Launch 5: 5
Integers 12 (0x100aaf600): 17 13 20 16 12 19 15 22 18 14 21 17
Integers 6 (0x100aaf670): 11 7 14 10 6 13
Integers 9 (0x100aaf6d0): 14 10 17 13 9 16 12 19 15
Integers 2 (0x100aaf740): 7 3
Launch 6: 8
Integers 5 (0x100aaf790): 10 6 13 9 5
Launch 7: 11
Integers 8 (0x100aaf7f0): 13 9 16 12 8 15 11 18
Launch 8: 1
Integers 11 (0x100aaf850): 16 12 19 15 11 18 14 21 17 13 20
Launch 9: 4
==3412==
==3412== Process terminating with default action of signal 11 (SIGSEGV)
==3412== Access not within mapped region at address 0x700003062C1A
==3412== at 0x10046F374: _pthread_find_thread (in /usr/lib/system/libsystem_pthread.dylib)
==3412== by 0x10046F2CF: _pthread_lookup_thread (in /usr/lib/system/libsystem_pthread.dylib)
==3412== by 0x10047061A: pthread_join (in /usr/lib/system/libsystem_pthread.dylib)
==3412== by 0x100000C3C: main (pth37.c:52)
==3412== If you believe this happened as a result of a stack
==3412== overflow in your program's main thread (unlikely but
==3412== possible), you can try to increase the size of the
==3412== main thread stack using the --main-stacksize= flag.
==3412== The main thread stack size used in this run was 8388608.
--3412:0:schedule VG_(sema_down): read returned -4
==3412==
==3412== HEAP SUMMARY:
==3412== in use at exit: 26,557 bytes in 196 blocks
==3412== total heap usage: 280 allocs, 84 frees, 32,789 bytes allocated
==3412==
Memcheck: mc_leakcheck.c:1106 (void lc_scan_memory(Addr, SizeT, Bool, Int, Int, Addr, SizeT)): Assertion 'bad_scanned_addr >= VG_ROUNDUP(start, sizeof(Addr))' failed.
host stacktrace:
==3412== at 0x238050773: ???
==3412== by 0x238050B9C: ???
==3412== by 0x238050B7A: ???
==3412== by 0x238003B86: ???
==3412== by 0x2380033A6: ???
==3412== by 0x238002050: ???
==3412== by 0x238014F0D: ???
==3412== by 0x23805D562: ???
==3412== by 0x2380F2772: ???
==3412== by 0x2380F287A: ???
sched status:
running_tid=2
出于礼貌,我对此感到困惑。
你正在做的事情正在引起问题。pthread_create
返回一个错误代码。从手册页:
如果成功,
pthread_create()
返回0;当出现错误时,它返回一个错误, *线程的内容未定义。
你不能这样返回指向malloc
的指针。你必须有一个全局的指针数组,你的线程可以保存它的malloc
结果,或者你必须传递一个指针作为参数。如果你有超过1个参数,你想传递给一个线程,我知道如何做到这一点的唯一方法是使用struct
。