POSIX多线程程序设计学习篇之一(线程管理)

本文介绍了多线程编程的基础知识,包括线程的创建、管理、分离及栈管理等内容,并通过示例代码详细展示了如何使用pthread库进行多线程编程。

首先来一份最简单的开胃篇代码来认识多线程

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

void *thread_routine(void *arg)//子线程执行的函数
{
     printf("son thread\n");
     int i = 0;
     for(i = 0; i<100; i++)
          printf("i =%d\n",i);

     return arg;
}

void main(int argc,char *argv[])
{
    int a = 0;
    printf("a = %d\n",a);
    
    pthread_t thread_id;
    void *thread_result;
    int status;
    
    status = pthread_create(&thread_id,NULL,thread_routine,NULL);//创建子线程
    if(status != 0)
    	printf("create thread error");
    else 
        printf("create thread success%ld\n",(unsigned long)thread_id);
     
     status = pthread_join(thread_id,&thread_result);//阻塞主线程,直到子线程执行完毕。
}


一,Pthread API 命名约定如下:





二,线程的创建、退出等函数介绍

pthread_create(thread,attr,function,arg)//线程的创建(线程结构变量,属性,线程函数入口,线程函数参数)

pthread_exit(status)

pthread_attr_init(attr)

pthread_attr_destroy(attr)

一个进程可以创建的线程最大数量取决于系统实现

线程属性:  

  • 线程被创建时会带有默认的属性。其中的一些属性可以被程序员用线程属性对象来修改。 
  • pthread_attr_init 和 pthread_attr_destroy用于初始化/销毁先前属性对象。 
  • 其它的一些函数用于查询和设置线程属性对象的指定属性。 
  • 其他等等

结束终止:

     a, 线程从主线程(main函数的初始线程)返回。

     b, 线程调用了pthread_exit函数,显示的退出当前线程。

     c, 其它线程使用 pthread_cancel函数结束线程。

     d, 调用exec或者exit函数,整个进程结束。

如果main()在其他线程结束前用pthread_exit()退出了,其他线程将会继续执行。否则,他们会随着main的结束而终止。


三,链接和分离

1,在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。
joinable:能够被其他线程收回其资源和杀死;在被其他线程回收之前,系统资源是不释放的。
          当pthread_join()函数返回时,创建的线程才算终止。(系统默认)
detached:不能被其他线程回收或杀死的,系统资源在它终止时由系统自动释放。

线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的。

2,常用函数如下:

(1)线程的阻塞和分离

int pthread_join(pthread_t tid, void**thread_return); //阻塞本地线程,直到设置的线程tid执行结束。
int pthread_detach(pthread_t tid); //设置分离状态的方式
为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

(2)设置和获得分类状态
pthread_attr_setdetachstate (attr,detachstate)
pthread_attr_getdetachstate (attr,detachstate)
其中detachstate参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)

(3)分离线程有个问题是,可能在pthread_create函数返回之前就终止了,就可能将线程ID和系统资源移交给其他的线程使用,
这样当前调用pthread_create的线程就得到了错误的线程号。(可以在被创建的线程里调用pthread_cond_timewait函数避免)

3,设置分离状态的方式
(1)调用 pthread_detach()函数
(2)创建线程时就将线程属性设置为detached 状态
   首先声明pthread_attr_t attr;
   然后初始化attr---pthread_attr_init(&attr);
   再然后设置线程类别---pthread_attr_setdetachstate(&attr,detachstatus);

   最后线程create完毕后释放attr--pthread_attr_destroy(&attr);

下面例子足以说明以上信息

#include <pthread.h> 
#include <stdio.h> 
#define NUM_THREADS  (3) 

#define JOIN_TO_DETACH (1)//三个宏只有一个能为1,测试三种情况
#define ONLY_JOIN (0)
#define ONLY_DETACH (0)

void *fun(void *arg) 
{  

#if JOIN_TO_DETACH
   int status = pthread_detach(pthread_self());
   printf("detach %d status= %x\n",((int*)arg)[0], (long)status); 
#endif

   int i = 0;
   for (i=0; i<5; i++) 
   { 
     printf("id = %d result = %d\n",((int*)arg)[0],i); 
     sched_yield();//将处理器交给另一个等待的程序
   } 

   //pthread_exit(NULL); 
} 

 
int main (int argc, char *argv[]) 
{ 
   pthread_t thread[NUM_THREADS]; 
   pthread_attr_t attr;
   int a[NUM_THREADS][1]; 
   int i; 
   int status = 0;

   pthread_attr_init(&attr);

#if JOIN_TO_DETACH || ONLY_JOIN
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); //设置成结合属性
#else
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 
#endif

   for(i=0; i<NUM_THREADS; i++) 
   { 
      a[i][0] = i;
      printf("Creating thread %d\n", i); 
      pthread_create(&thread[i], &attr, fun, a[i]); 
   } 
   
   pthread_attr_destroy(&attr); 

#if ONLY_JOIN || DETACH_TO_JOIN
   for(i=0; i<NUM_THREADS; i++) 
   { 
       status = pthread_join(thread[i],NULL);
       printf("join %d status= %x\n",i, (long)status); 
   } 
#endif

   pthread_exit(NULL); 
   return 0;
} 



四,栈管理
pthread_attr_getstacksize (attr, stacksize)  
pthread_attr_setstacksize (attr, stacksize)  
pthread_attr_getstackaddr (attr, stackaddr)  
pthread_attr_setstackaddr (attr, stackaddr)
可以被程序用于将栈设置在指定的内存区域


五,补充

1,线程ID

pthread_self ()  //返回调用该函数的线程的唯一,系统分配的线程ID。
pthread_equal (thread1,thread2) // 比较两个线程ID,若不同返回0,否则返回非0值

 
注意:

(1)这两个函数中的线程ID对象是不透明的,不是轻易能检查的。因为线程ID是不透明的对象,所以C语言的==操作符不能用于比较 两个线程ID。

(2)gettid():是内核给线程(轻量级进程)分配的进程id,全局(所有进程中)唯一;//#include <sys/types.h>

(3)pthread_self():是在用户态实现的,获取的id实际上是主线程分配给子线程的线程描述符的地址而已,只是在当前进程空间中是唯一的。

#include <pthread.h>     
#include <stdlib.h>     
#include <stdio.h>     
#include <sys/syscall.h>  
#include <sys/types.h>  
void *fun(void *arg)    
{    
    pid_t pid;    
    pthread_t tid;    
    pid = getpid();    
    tid = pthread_self();    
    printf("%s pid is:%d ;tid is :%d\n", (char*)arg,(unsigned int)pid, (unsigned int)tid, (unsigned int)tid);   
    return 0;    
}    
  
int main()  
{  
    char *s = "son thread";
    pthread_t thread;
    pthread_create(&thread, NULL, fun, s);    
    printf("in main thread_id is :%d\n",thread);  
    printf("in main pid is:%d ;tid is: %d \n",(unsigned int)getpid(),(unsigned int)pthread_self(),(unsigned int)pthread_self());  
    sleep(3);  
    return 0;  
}

2,线程执行顺序

(1)线程的优先级无法保障线程的执行次序。只不过优先级高的线程获取 CPU 资源的概率大一点而已.

(2)setitimer()和signal()。可以精确控制线程的运行时间。
   如果没有设置条件,是不能保证线程运行次序的,操作系统会自行随机安排


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值