linux多线程设计

linux多线程设计是指基于Linux操作系统下的多线程设计,包括多任务程序的设计,并发程序设计,网络程序设计,数据共享等。Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。

pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:

  typedef unsigned long int pthread_t;

  它是一个线程的标识符。函数pthread_create用来创建一个线程,它的原型为:

  extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,

  void *(*__start_routine) (void *), void *__arg));

  第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

  函数pthread_join用来等待一个线程的结束。函数原型为:

  extern int pthread_join __P ((pthread_t __th, void **__thread_return));

  第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:

  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));

  唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。

  #include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

#define THREAD_NUMBER 3   //线程数
#define REPEAT_NUMBER 5    //每个线程中的小任务数
#define DELAY_TIME_LEVELS 10.0    //小任务之间的最大时间间隔

void *thrd_func(void *arg)    //线程函数例程
{
 int thrd_num = (int)arg;
 int delay_time = 0;
 int count = 0;      //执行时线程共享

 printf("Thread %d is starting/n",thrd_num);
 for(count = 0;count < REPEAT_NUMBER;count++)
 {
  delay_time = (int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)) + 1;   //时间随即
  sleep(delay_time);
  printf("/tThread %d:job %d delay = %d/n",thrd_num,count,delay_time);
 }
 printf("Thread %d finished/n",thrd_num);
 pthread_exit(NULL);    //线程退出
}

int main()
{
 pthread_t thread[THREAD_NUMBER];   //线程数
 int no = 0,res;
 void *thrd_ret;
 
 srand(time(NULL));
 
 for(no = 0; no < THREAD_NUMBER;no++)   //创建多线程
 {
  res = pthread_create(&thread[no],NULL,thrd_func,(void*)no);    //调用函数thrd_func()
  if(res!=0)
  {
   printf("Create thread %d failed/n",no);
   exit(res);
  }
 }
 printf("Create threads success/n Waiting for threads to finish.../n");
 for(no = 0;no<THREAD_NUMBER;no++) 
 {
  res = pthread_join(thread[no],&thrd_ret);    //用于将当前线程挂起来等待线程的结束
  if(!res)
  {
   printf("Thread %d joined/n",no);
  }
  else
  {
   printf("Thread %d join failed/n",no);
  }
 }
 return 0;
}

 

互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。

斥锁变量mutex,结构pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

#define THREAD_NUMBER 3
#define REPEAT_NUMBER 5
#define DELAY_TIME_LEVELS 10.0
pthread_mutex_t mutex;

void *thrd_func(void *arg)
{
 int thrd_num = (int)arg;
 int delay_time = 0;
 int count = 0;
 int res;

 res = pthread_mutex_lock(&mutex);   //互斥锁上锁
 if(res)
 {
  printf("Thread %d lock failed/n",thrd_num);
  pthread_exit(NULL);
 }

 printf("Thread %d is starting/n",thrd_num);
 for(count = 0;count < REPEAT_NUMBER;count++)
 {
  delay_time = (int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
  sleep(delay_time);
  printf("/tThread %d:job %d delay = %d/n",thrd_num,count,delay_time);
 }
 printf("Thread %d finished/n",thrd_num);
 pthread_exit(NULL);
}

int main()
{
 pthread_t thread[THREAD_NUMBER];
 int no = 0,res;
 void *thrd_ret;
 
 srand(time(NULL));   //随机数种子
 
 pthread_mutex_init(&mutex,NULL);    //互斥锁初始化
 for(no = 0; no < THREAD_NUMBER;no++)
 {
  res = pthread_create(&thread[no],NULL,thrd_func,(void*)no);
  if(res!=0)
  {
   printf("Create thread %d failed/n",no);
   exit(res);
  }
 }
 printf("Create threads success/n Waiting for threads to finish.../n");
 for(no = 0;no<THREAD_NUMBER;no++)
 {
  res = pthread_join(thread[no],&thrd_ret);
  if(!res)
  {
   printf("Thread %d joined/n",no);
  }
  else
  {
   printf("Thread %d join failed/n",no);
  }
  pthread_mutex_unlock(&mutex);   //互斥锁解锁
 }
 pthread_mutex_destroy(&mutex);
 return 0;
}

 

属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义,喜欢追根问底的人可以自己去查看。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

  关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。

  设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。

线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

  另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

#define REPEAT_NUMBER 3
#define DELAY_TIME_LEVELS 10.0

int finish_flag = 0;    //结束标志变量

void *thrd_func(void *arg)   //线程执行函数
{
 int delay_time = 0;
 int count = 0;
 
 printf("Thread is starting/n");
 for(count = 0;count < REPEAT_NUMBER;count++)
 {
  delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
  sleep(delay_time);
  printf("/tThread:job %d delay = %d/n",count,delay_time);
 }
 printf("Thread finished/n");
 finish_flag = 1;
 pthread_exit(NULL);
}

int main()
{
 pthread_t thread;
 pthread_attr_t attr;
 int no = 0,res;
 void *thrd_ret;
 
 srand(time(NULL));
 
 res = pthread_attr_init(&attr);   //初始化线程属性对象
 if(res!=0)
 {
  printf("Create attribute failed/n");
  exit(res);
 }
 
 res = pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);  //设置线程绑定属性
 res += pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); //设置线程分离属性
 if(res!=0)
 {
  printf("Setting attribute failed/n");
  exit(res);
 }
 res = pthread_create(&thread,&attr,thrd_func,NULL);
 if(res!=0)
 {
  printf("Create thread failed/n");
  exit(res);
 }

 pthread_attr_destroy(&attr);  //释放线程属性对象
 printf("Create tread success/n");

 while(!finish_flag)
 {
  printf("Waiting for thread to finish.../n");
  sleep(2);
 }
 return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值