Linux多线程编程实例解析

  Linux系统下的多线程遵循POSIX线程接口,称为

pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux

下pthread的实现是通过系统调用clone()来实现的。clone()是

Linux所特有的系统调用,它的使用方式类似fork,关于clone()的详细情况,有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的 多线程程序

pthread_create.c。

  一个重要的线程创建函数原型:

  #include

  int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict

attr, void *(*start_rtn)(void),void *restrict arg);

  返回值:若是成功建立线程返回0,否则返回错误的编号

  形式参数:

  pthread_t *restrict tidp 要创建的线程的线程id指针

  const pthread_attr_t *restrict attr 创建线程时的线程属性

  void* (start_rtn)(void) 返回值是void类型的指针函数

  void *restrict arg start_rtn的行参

  例程1:

  功能:创建一个简单的线程

  程序名称:pthread_create.c

  代码如下:

  #include

  #include

  void *mythread1(void)

  {

  int i;

  for(i = 0; i < 10; i++)

  {

  printf("This is the 1st pthread,created by xiaoqiang!\n");

  sleep(1);

  }

  }

  void *mythread2(void)

  {

  int i;

  for(i = 0; i < 10; i++)

  {

  printf("This is the 2st pthread,created by xiaoqiang!\n");

  sleep(1);

  }

  }

  int main(int argc, const char *argv[])

  {

  int i = 0;

  int ret = 0;

  pthread_t id1,id2;

  ret = pthread_create(&id1, NULL, (void *)mythread1,NULL);

  if(ret)

  {

  printf("Create pthread error!\n");

  return 1;

  }

  ret = pthread_create(&id2, NULL, (void *)mythread2,NULL);

  if(ret)

  {

  printf("Create pthread error!\n");

  return 1;

  }

  pthread_join(id1,NULL);

  pthread_join(id2,NULL);

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ vi thread1.c

  fs@ubuntu:~/qiang/thread$ gcc -o thread1 thread1.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread1

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 2st pthread,created by xiaoqiang!

  This is the 1st pthread,created by xiaoqiang!

  fs@ubuntu:~/qiang/thread$

  两个线程交替执行。

  另外,因为pthread的库不是linux系统的库,所以在进行编译的时候要加上-lpthread,否则编译不过,会出现下面错误

  thread_test.c: 在函数 ‘create’ 中:

  thread_test.c:7: 警告: 在有返回值的函数中,程序流程到达函数尾

  /tmp/ccOBJmuD.o: In function

`main':thread_test.c:(.text+0x4f):对‘pthread_create’未定义的引用

  collect2: ld 返回 1

  此例子介绍了创建线程的方法

  下面例子介绍向线程传递参数。

  例程2:

  功能:向新的线程传递整形值

  程序名称:pthread_int.c

  代码如下:

  #include

  #include

  void *create(void *arg)

  {

  int *num;

  num = (int *)arg;

  printf("Create parameter is %d\n",*num);

  return (void *)0;

  }

  int main(int argc, const char *argv[])

  {

  pthread_t id1;

  int error;

  int test = 4;

  int *attr = &test;

  error = pthread_create(&id1,NULL,create,(void *)attr);

  if(error)

  {

  printf("Pthread_create is not created!\n");

  return -1;

  }

  sleep(1);

  printf("Pthread_create is created..\n");

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ vi thread2.c

  fs@ubuntu:~/qiang/thread$ gcc -o thread2 thread2.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread2

  Create parameter is 4

  Pthread_create is created..

  fs@ubuntu:~/qiang/thread$

  例程总结:

  可以看出来,我们在main函数中传递的整行指针,传递到我们新建的线程函数中。

  在上面的例子可以看出来我们向新的线程传入了另一个线程的int数据,线程之间还可以传递字符串或是更复杂的数据结构。

  例程3:

  程序功能:向新建的线程传递字符串

  程序名称:pthread_string.c

  代码如下:

  #include

  #include

  void *create(char *arg)

  {

  char *str;

  str = arg;

  printf("The parameter passed from main is %s\n",str);

  return (void *)0;

  }

  int main()

  {

  int error;

  pthread_t id1;

  char *str1 = "Hello ,xiaoqiang!";

  char *attr = str1;

  error = pthread_create(&id1, NULL, create, (void *)attr);

  if(error != 0)

  {

  printf("This pthread is not created!\n");

  return -1;

  }

  sleep(1);

  printf("pthread is created..\n");

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ ./thread3

  The parameter passed from main is Hello ,xiaoqiang!

  pthread is created..

  fs@ubuntu:~/qiang/thread$

  例程总结:

  可以看出来main函数中的字符串传入了新建的线程中。

  例程4:

  程序功能:向新建的线程传递字符串

  程序名称:pthread_struct.c

  代码如下:

  #include

  #include

  #include

  struct menber

  {

  int a;

  char *s;

  };

  void *create(void *arg)

  {

  struct menber *temp;

  temp = (struct menber *)arg;

  printf("menber->a = %d\n",temp->a);

  printf("menber->s = %s\n",temp->s);

  return (void *)0;

  }

  int main()

  {

  int error;

  pthread_t id1;

  struct menber *p;

  p = (struct menber *)malloc(sizeof(struct menber));

  p->a = 1;

  p->s = "xiaoqiang!";

  error = pthread_create(&id1,NULL,create,(void *)p);

  if(error)

  {

  printf("pthread is not created!\n");

  return -1;

  }

  sleep(1);

  printf("pthread is created!\n");

  free(p);

  p = NULL;

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ vi thread4.c

  fs@ubuntu:~/qiang/thread$ gcc -o thread4 thread4.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread4

  menber->a = 1

  menber->s = xiaoqiang!

  pthread is created!

  fs@ubuntu:~/qiang/thread$

  例程总结:

  可以看出来main函数中的一个结构体传入了新建的线程中。

  线程包含了标识进程内执行环境必须的信息。他集成了进程中的所有信息都是对线程进行共享的,包括文本程序、程序的全局内存和堆内存、栈以及文件描述符

  例程5:

  程序目的:验证新建立的线程可以共享进程中的数据

  程序名称:pthread_share.c

  代码如下:

  #include

  #include

  static int a = 5;

  void *create(void *arg)

  {

  printf("New pthread...\n");

  printf("a = %d\n",a);

  return (void *)0;

  }

  int main(int argc, const char *argv[])

  {

  int error;

  pthread_t id1;

  error = pthread_create(&id1, NULL, create, NULL);

  if(error != 0)

  {

  printf("new thread is not created!\n");

  return -1;

  }

  sleep(1);

  printf("New thread is created...\n");

  return 0;

  }

  结果如下:

  fs@ubuntu:~/qiang/thread$ vi thread5.c

  fs@ubuntu:~/qiang/thread$ gcc -o thread5 thread5.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread5

  New pthread...

  a = 5

  New thread is created...

  fs@ubuntu:~/qiang/thread$

  例程总结:

  可以看出来,我们在主线程更改了我们的全局变量a的值的时候,我们新建立的线程则打印出来了改变的值,可以看出可以访问线程所在进程中的数据信息。

  2、线程的终止

  如果进程中任何一个线程中调用exit,_Exit,或者是_exit,那么整个进程就会终止,

  与此类似,如果信号的默认的动作是终止进程,那么,把该信号发送到线程会终止进程。

  线程的正常退出的方式:

  (1) 线程只是从启动例程中返回,返回值是线程中的退出码

  (2) 线程可以被另一个进程进行终止

  (3) 线程自己调用pthread_exit函数

  两个重要的函数原型:

  include

  void pthread_exit(void *rval_ptr);

  /*rval_ptr 线程退出返回的指针*/

  int pthread_join(pthread_t thread,void **rval_ptr);

  /*成功结束进程为0,否则为错误编码*/

  pthread_join使一个线程等待另一个线程结束。

  代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

  头文件 : #include

  函数定义: int pthread_join(pthread_t thread, void **retval);

  描述

:pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

  参数 :thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。

  返回值 : 0代表成功。 失败,返回的则是错误号。

  例程6

  程序目的:线程正常退出,接受线程退出的返回码

  程序名称:pthread_exit.c

  执行代码如下:

  #include

  #include

  #include

  void *create(void *arg)

  {

  printf("new thread is created ... \n");

  return (void *)0;

  }

  int main(int argc,char *argv[])

  {

  pthread_t tid;

  int error;

  void *temp;

  error = pthread_create(&tid, NULL, create, NULL);

  if( error )

  {

  printf("thread is not created ... \n");

  return -1;

  }

  error = pthread_join(tid, &temp);

  if( error )

  {

  printf("thread is not exit ... \n");

  return -2;

  }

  printf("thread is exit code %d \n", (int )temp);

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ vi thread6.c

  fs@ubuntu:~/qiang/thread$ gcc -o thread6 thread6.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread6

  new thread is created ...

  thread is exit code 0

  fs@ubuntu:~/qiang/thread$

  例程总结:

  可以看出来,线程退出可以返回线程的int数值。

  线程退出不仅仅可以返回线程的int数值,还可以返回一个复杂的数据结构

  例程7

  程序目的:线程结束返回一个复杂的数据结构

  代码如下:

  #include

  #include

  #include

  struct menber

  {

  int a;

  char *b;

  }temp={8,"xiaoqiang"};

  void *create(void *arg)

  {

  printf("new thread ... \n");

  return (void *)&temp;

  }

  int main(int argc,char *argv[])

  {

  int error;

  pthread_t tid;

  struct menber *c;

  error = pthread_create(&tid, NULL, create, NULL);

  if( error )

  {

  printf("new thread is not created ... \n");

  return -1;

  }

  printf("main ... \n");

  error = pthread_join(tid,(void *)&c);

  if( error )

  {

  printf("new thread is not exit ... \n");

  return -2;

  }

  printf("c->a = %d \n",c->a);

  printf("c->b = %s \n",c->b);

  sleep(1);

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ gcc -o thread7 thread7.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread7

  main ...

  new thread ...

  c->a = 8

  c->b = xiaoqiang

  fs@ubuntu:~/qiang/thread$

  例程总结:

  一定要记得返回的数据结构要是在这个数据要返回的结构没有释放的时候应用,如果数据结构已经发生变化,那返回的就不会是我们所需要的,而是脏数据。

  3、线程标识

  函数原型:

  #include

  pthread_t pthread_self(void);

  pid_t getpid(void);

  getpid()用来取得目前进程的进程识别码,函数说明

  例程8

  程序目的:实现在新建立的线程中打印该线程的id和进程id

  代码如下:

  #include

  #include

  #include /*getpid()*/

  void *create(void *arg)

  {

  printf("New thread .... \n");

  printf("This thread's id is %u \n", (unsigned int)pthread_self());

  printf("The process pid is %d \n",getpid());

  return (void *)0;

  }

  int main(int argc,char *argv[])

  {

  pthread_t tid;

  int error;

  printf("Main thread is starting ... \n");

  error = pthread_create(&tid, NULL, create, NULL);

  if(error)

  {

  printf("thread is not created ... \n");

  return -1;

  }

  printf("The main process's pid is %d \n",getpid());

  sleep(1);

  return 0;

  }

  执行结果如下:

  fs@ubuntu:~/qiang/thread$ gcc -o thread8 thread8.c -lpthread

  fs@ubuntu:~/qiang/thread$ ./thread8

  Main thread is starting ...

  The main process's pid is 4955

  New thread ....

  This thread's id is 3075853120

  The process pid is 4955

  fs@ubuntu:~/qiang/thread$

  最后提供一些参考资料

  linux多线程编程

  http://www.makeru.com.cn/course/details/1937?s=45051

  循环链表及线性表的应用

  http://www.makeru.com.cn/course/details/1902?s=45051

  linux基础

  http://www.makeru.com.cn/course/details/2058?s=45051

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349