關於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 2

char **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的結果都一樣。

你代碼改成這樣試試:

#include &
#include &


#include &
#define N 2

char **ptr; /* Global variable */

void* thread(void* vargp)
{
int myid = (int)((size_t)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_join(tid, NULL); } pthread_exit(NULL); }

我的運行結果是這樣的:

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++ -v

Using built-in specs.

COLLECT_GCC=g++

COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.6/lto-wrapper

Target: x86_64-linux-gnu

Configured with: ../src/configure -v --with-pkgversion="Ubuntu/Linaro 4.6.3-1ubu

ntu5" --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-incl

uded-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6

--libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-li

bstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-pl

ugin --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 --targ

et=x86_64-linux-gnu

Thread model: posix

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)


那個thead裡面的指針轉成int貌似是寫錯了。

不應該是先將void*轉成int*再在前面加*嗎。

int mytid =*(int*)varg


和題主同樣代碼的時候,第一次運行正確,後面就變成了。。。

加了jion之後則是

完美運行那麼為什麼出現問題呢 猜測 ptr 雖然是全局變數然而他是指向msg的指針,可能是msg出問題了(後來發現贊數最多的第一句就一針見血已經說了。。。)

總之最後直接賦值給ptr繞過了msg之後結果就沒問題了

所以jion是真的在等其他人,phtread_exit()是自己這個線程已經跑了,留下了進程讓剩下的能夠繼續。。。


tid[N]


推薦閱讀:

做CSAPP遇到點問題,希望能指點一番?
如何取得32位無符號數二進位表示是否含有奇數個1?

TAG:C編程語言 | 多線程 | CSAPP |