a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 137|回复: 2

[综合辅导] Linux辅导:线程间共享变量的注意事项

[复制链接]
发表于 2012-8-4 12:07:07 | 显示全部楼层 |阅读模式
两个线程共享同一个变量是否需要加锁,   根据需要来定。
5 N' }& A- p; e( I/ K$ A  但是如果两个线程有可能同时修改这个变量的话,, q& d0 V  H' V6 b7 ?. L3 f1 L* F; t  a
  那么一定是需要加锁的。: U7 D( F2 @' F! R9 E0 a; |$ U& Q: C
  代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。5 y+ h- [8 N2 B% @' r+ Y  G
  pthread_join函数及linux线程2 l' u* x' J+ }* a/ G
  pthread_join使一个线程等待另一个线程结束。  C3 w  K' E( @( K+ I  p+ ]
  代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
) w5 b5 j  m; i! E  所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。
) _$ m! V/ |- ]0 c1 ]) U5 ~  下面说一下如何创建一个线程。" u# W+ e! g' [5 V0 z
  通过创建线程,线程将会执行一个线程函数,该线程格式必须按照下面来声明:
4 p/ p) c. O3 ^) f0 r  void * Thread_Function(void *)
) W7 x+ Y1 H. }* E7 Z% r# `4 ]9 X  创建线程的函数如下:5 X: X+ ~  }8 i) J% U  k) n+ J
  int pthread_create(pthread_t *restrict thread,1 w. G6 o9 ^& p9 T# E
  const pthread_attr_t *restrict attr,
: \+ y) m6 O& ^& }: f/ c  void *(*start_routine)(void*), void *restrict arg);
2 Q+ }) C& r4 G$ \* A! M# a1 b  下面说明一下各个参数的含义:
+ N2 ]! H1 E: A) z2 \. j% l1 O3 k  thread:所创建的线程号。# X0 A5 ^3 H6 X9 O- a( H+ c- z# ?
  attr:所创建的线程属性,这个将在后面详细说明。! v# l1 o9 S2 F2 I* Q
  start_routine:即将运行的线程函数。& Q+ X! y( i" y2 q+ U5 j- R9 |
  art:传递给线程函数的参数。. k# R: |! d5 e: p
  下面是一个简单的创建线程例子:; b% B3 E  ]8 K. \: K/ X
  #include $ Q4 T" K& @2 R  L8 a  O' b( S
  #include
- S2 S& j7 ?8 l* K; A  /* Prints x’s to stderr. The parameter is unused. Does not return. */" T1 a) M: G  j( E. @, W, G
  void* print_xs (void* unused)
0 L# k9 _9 V5 C9 j( h  {  t7 N. v+ k. s6 W2 v
  while (1)
3 j& L: m( z0 `/ n* q$ Q  fputc (‘x’, stderr);- |: _4 W0 W" [, i4 Y
  return NULL;& V$ w3 U. `' K8 B7 H! k
  }
1 N; Z2 ?4 u& ?$ o  /* The main program. */9 j$ A2 g7 L7 ?% {, v7 K
  int main ()
" Q# t8 v- D3 ]4 R  {5 O2 {( n3 G+ ?5 f; F
  pthread_t thread_id;! w1 |) ]: v+ k& d- d( |8 L
  /* Create a new thread. The new thread will run the print_xs: N, l  U& x/ `" ^- K' W+ H5 a. Y
  function. */
/ ^. n7 B1 T8 N& ~3 F8 {2 i% C  pthread_create (&thread_id, NULL, &print_xs, NULL);& _. ~+ [% A! Q( ?2 N! Z. D# L5 Q( p" W
  /* Print o’s continuously to stderr. */
& p% J7 |7 `9 w8 x  while (1)# W' `* A# r3 t/ `6 y- S
  fputc (‘o’, stderr);
( `5 E6 j0 _- e+ H$ E- r7 l  return 0;1 V$ C( H( o2 J

# a$ j3 b: \* P; @2 R6 m3 Z, ~9 e  }
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:07:08 | 显示全部楼层

Linux辅导:线程间共享变量的注意事项

</p>  在编译的时候需要注意,由于线程创建函数在libpthread.so库中,所以在编译命令中需要将该库导入。命令如下:
4 l" i' D5 g7 d: k, {; E( X  gcc –o createthread –lpthread createthread.c
0 b9 u0 D$ f1 A6 ~  如果想传递参数给线程函数,可以通过其参数arg,其类型是void *。如果你需要传递多个参数的话,可以考虑将这些参数组成一个结构体来传递。另外,由于类型是void *,所以你的参数不可以被提前释放掉。
- S/ R& g* T% t, b, U+ K% U/ ]  下面一个问题和前面创建进程类似,不过带来的问题回避进程要严重得多。如果你的主线程,也就是main函数执行的那个线程,在你其他县城推出之前就已经退出,那么带来的bug则不可估量。通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出。
& `' [: u( `# D+ e, l  int pthread_join(pthread_t thread, void **value_ptr);
7 ]" ?* o, N' z3 X  thread:等待退出线程的线程号。. l+ A! f2 ^% t0 [4 F( Q
  value_ptr:退出线程的返回值。- F- I' |1 y" s. y
  下面一个例子结合上面的内容:
9 ~% D7 N( b3 f  f) R& G) k, b  int main ()
# J: H1 a/ \: K: E5 j1 `  {
  h9 x2 g2 n6 r, F. o& s( o& B  pthread_t thread1_id;
7 A' e- M  o* B2 i$ `  pthread_t thread2_id;
9 Y- v- H, H# Q, C  struct char_print_parms thread1_args;9 B3 H. G2 j! e0 r
  struct char_print_parms thread2_args;
! v* @* t+ l) h  /* Create a new thread to print 30,000 x’s. */+ t1 R) q  N6 k8 h# w6 j
  thread1_args.character = ’x’;$ h4 [. o+ u5 f9 D
  thread1_args.count = 30000;: Z. d  e$ f% U' S& c. C
  pthread_create (&thread1_id, NULL, &char_print, &thread1_args);
, u: _+ u0 s% W9 c# M  /* Create a new thread to print 20,000 o’s. */
. L- @. u( m, b  thread2_args.character = ’o’;
) ^! I  N- r- s5 l  thread2_args.count = 20000;; M1 v* s( M) u$ {3 O7 H; U3 W
  pthread_create (&thread2_id, NULL, &char_print, &thread2_args);6 L- R+ E$ o4 Q8 |' n, m) V
  /* Make sure the first thread has finished. */
# P& W/ o" w  Q. i7 t- U7 O  pthread_join (thread1_id, NULL);# X$ p2 }; E8 W
  /* Make sure the second thread has finished. */! {, f% H5 @+ g/ x3 s. F5 Q9 I
  pthread_join (thread2_id, NULL);
. _  _( u4 L/ J: t% N7 ]  /* Now we can safely return. */
) V! [+ Y( s! X% J: ]# V  return 0;2 T  Y; [9 G' n; U2 U8 F
  }2 ?$ N  C  z; V: n$ ?
  下面说一下前面提到的线程属性。* b/ e" E! b0 L, {  t/ ]% c& O
  在我们前面提到,可以通过pthread_join()函数来使主线程阻塞等待其他线程退出,这样主线程可以清理其他线程的环境。但是还有一些线程,更喜欢自己来清理退出的状态,他们也不愿意主线程调用pthread_join来等待他们。我们将这一类线程的属性称为detached。如果我们在调用pthread_create()函数的时候将属性设置为NULL,则表明我们希望所创建的线程采用默认的属性,也就是jionable。如果需要将属性设置为detached,则参考下面的例子:
( u9 b/ a* A& v  #include
5 \3 c% ?5 h3 B- N8 q! {  #include 7 _0 Q  C) p: `) ~# E; q  d; c
  void * start_run(void * arg)( k0 ^% G* Q% g: _% m$ _, ^! X
  {% F6 v; o' H* M; {
  //do some work* h5 m6 Q2 C( w/ |0 K" W
  }: M, p0 B4 v* o
  int main()
4 l% |0 E7 {8 \0 t7 i6 a, B* d7 E  {
$ K! d  s/ K- z, B  pthread_t thread_id;
+ ]/ u+ x- `, P3 r" B  pthread_attr_t attr;
4 x8 P8 D8 l9 H5 b" K3 U  pthread_attr_init(&attr);* v1 \% Y& O( \2 ~. C- S0 W/ S
  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);, L! V' I0 h+ ?1 x$ v; ^
  pthread_create(&thread_id,&attr,start_run,NULL);! p+ |2 r& u/ N' ~
  pthread_attr_destroy(&attr);, s8 a& F, M- v% z2 v6 C
  sleep(5);
7 v' P+ v/ C+ z# N  exit(0);
: I* Y3 r, ?* B9 A( Y' j2 t- a0 G# x& R6 E' H
  }
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:07:09 | 显示全部楼层

Linux辅导:线程间共享变量的注意事项

</p>  在线程设置为joinable后,可以调用pthread_detach()使之成为detached。但是相反的操作则不可以。还有,如果线程已经调用pthread_join()后,则再调用pthread_detach()则不会有任何效果。
1 I0 a9 L4 s, p- Z, R3 Q  线程可以通过自身执行结束来结束,也可以通过调用pthread_exit()来结束线程的执行。另外,线程甲可以被线程乙被动结束。这个通过调用pthread_cancel()来达到目的。5 L7 f+ b" ^8 M0 P7 `
  int pthread_cancel(pthread_t thread);& b- F( r9 d2 a" c! l% e; P3 K
  函数调用成功返回0。5 G, C/ r* ]* Y6 v6 f7 w  ]
  当然,线程也不是被动的被别人结束。它可以通过设置自身的属性来决定如何结束。
; v% T% }( s( D0 n5 V) z  线程的被动结束分为两种,一种是异步终结,另外一种是同步终结。异步终结就是当其他线程调用 pthread_cancel的时候,线程就立刻被结束。而同步终结则不会立刻终结,它会继续运行,直到到达下一个结束点(cancellation point)。当一个线程被按照默认的创建方式创建,那么它的属性是同步终结。$ Y6 L& ]% Z, Q+ q2 _; q, {
  通过调用pthread_setcanceltype()来设置终结状态。
$ [! Z0 `* j/ s9 q5 g7 Z  int pthread_setcanceltype(int type, int *oldtype);
/ E7 s/ N4 ?7 J  b# R& T  state:要设置的状态,可以为PTHREAD_CANCEL_DEFERRED或者为PTHREAD_CANCEL_ASYNCHRONOUS。
% L- Y( ^7 Q5 X- K% q6 Y0 e" y  那么前面提到的结束点又是如何设置了?最常用的创建终结点就是调用pthread_testcancel()的地方。该函数除了检查同步终结时的状态,其他什么也不做。
, `7 G; e& F/ q) j  上面一个函数是用来设置终结状态的。还可以通过下面的函数来设置终结类型,即该线程可不可以被终结:
- b3 C( S- U5 D7 T  int pthread_setcancelstate(int state, int *oldstate);! V' S/ c" L- f
  state:终结状态,可以为PTHREAD_CANCEL_DISABLE或者PTHREAD_CANCEL_ENABLE。具体什么含义大家可以通过单词意思即可明白。7 e$ l- V2 i( Q0 ]0 G4 |
  最后说一下线程的本质。其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-21 14:50 , Processed in 0.398293 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表