關於CSAPP第12章並發編程的一個例子的疑惑?
問題現象描述:
主線程依次創建了2個peer thread,每個peer thread輸出對應的語句:第一個被創建的peer thread輸出對應msgs[0]即"Hello from foo";第二個被創建的peer thread輸出對應msgs[1]即"Hello from bar".但在ubuntu14.04_i386機器上運行結果為:為什麼是「(null)」而不是「Hello from bar」?以下代碼(除頭文件)來自於CSAPP 2nd Edition, 12.4節的example.
#include &
#include &
#include &
#define N 2char **ptr; /* Global variable */
void* thread(void* vargp)
{
int myid = (int)vargp;
static int cnt = 0;
printf("[%d]: %s (cnt=%d)
", myid, ptr[myid], ++cnt);
return NULL;
}int main(int argc, char **argv)
{
int i;
pthread_t tid;
char *msgs[N] = {"Hello from foo", "Hello from bar"};
ptr = msgs;for (i = 0; i &< N; i++) pthread_create(tid, NULL, thread, (void*)i); pthread_exit(NULL); }
請教明白其中奧妙的同學。
這只是一個例子告訴你多線程共享數據的方式,並不是說這個例子應該產生你期望的結果。
出現這種現象的原因是ptr指向的msg是主線程上的臨時變數,主線程退出之後其內容不可知,可以是null,可以是垃圾數,還有可能棧本身不存在了搞出segfault。
為了保證thread執行時msg依然有效,main應該調用pthread_join等待線程執行完再退出。另一個陷阱是++cnt並不是原子操作,有可能兩個thread的cnt都是1,這個多開些線程就能見到了。int main(int argc, char **argv)
{
int i;
pthread_t tid;
char *msgs[N] = {"Hello from foo", "Hello from bar"};
ptr = msgs;
for (i = 0; i &< N; i++) { pthread_create(tid, NULL, thread, (void*)i); pthread_join(tid,NULL); // join the thread } pthread_exit(NULL); }
Ref: IBM Knowledge Center
我懷疑是沒有pthread_join()導致main thread的提前釋放,從而引發ptr指向的相關內容已被釋放。這可能是一個教材中的bug?因為這個問題貌似不是總是出現,因為我運行這段代碼的時候加不加join的結果都一樣。
你代碼改成這樣試試:char **ptr; /* Global variable */ void* thread(void* vargp) int main(int argc, char **argv) for (i = 0; i &< N; i++)
{
pthread_create(tid, NULL, thread, (void*)i);
pthread_join(tid, NULL);
}
pthread_exit(NULL);
}
#include &
#include &
#include &
#define N 2
{
int myid = (int)((size_t)vargp);
static int cnt = 0;
printf("[%d]: %s (cnt=%d)
", myid, ptr[myid], ++cnt);
return NULL;
}
{
int i;
pthread_t tid;
char *msgs[N] = {"Hello from foo", "Hello from bar"};
ptr = msgs;
我的運行結果是這樣的:
norman@Skeleton:~/test$ ./thread[0]: Hello from foo (cnt=1)[1]: Hello from bar (cnt=2)norman@Skeleton:~/test$ ./thread[0]: Hello from foo (cnt=1)[1]: Hello from bar (cnt=2)
norman@Skeleton:~/test$ ./thread[0]: Hello from foo (cnt=2)[1]: Hello from bar (cnt=1)norman@Skeleton:~/test$ ./thread[0]: Hello from foo (cnt=1)[1]: Hello from bar (cnt=2)g++的版本:norman@Skeleton:~/test$ g++ -vUsing built-in specs.COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.6/lto-wrapperTarget: x86_64-linux-gnuConfigured with: ../src/configure -v --with-pkgversion="Ubuntu/Linaro 4.6.3-1ubuntu5" --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared--enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6--libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic -
-enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnuThread model: posixgcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)那個thead裡面的指針轉成int貌似是寫錯了。不應該是先將void*轉成int*再在前面加*嗎。int mytid =*(int*)varg
和題主同樣代碼的時候,第一次運行正確,後面就變成了。。。
tid[N]
推薦閱讀: