Linux下C语言多线程编程

时间:2021-07-20 08:34:49

一、多线程的创建

1Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连 接时需要使用库libpthread.a。因此,后面的编译必须在选项中加入 -lpthread 选项,否则提示找不到pthread_create()这些函数。

Linuxpthread的实现是通过系统调用clone()来实现的。Clone()是linux所特有的系统调用者。

 

2pthread_t 是一个线程的标识符,创建线程用pthread_create(),等待线程结束用

函数原型:int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

第一个参数为指向线程标识符的指针,第二个用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个是运行函数的参数

pthread_join()

#include <stdio.h>

#include <pthread.h>

#include <stdlib.h>

 

void thread()

{

       int i;

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

       {

              printf("this is thread %d.n", i);

              sleep(i);

       }

}

 

int main(void)

{

       pthread_t id;

       int i, ret;

 

       ret = pthread_create(&id, NULL, (void *)thread, NULL);

       if (ret != 0)

       {

              printf("Create thread error!n");

              exit(1);

       }

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

       {

              printf("This is the main thread %d.n", i);

              sleep(i);

       }

 

       pthread_join(id, NULL);  // 线程阻塞函数,调用它的函数将一直等待到被等待的线程结束为止

       return 0;

}

二、属性

关于线程的绑定,牵涉到另外一个概念:轻进程(LWPLight Weight Process)。 轻进程可以理解为内核线程,它位于用户层和系统层之间系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程

绑定状况下,则顾名思义,即某个线程固定的""在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设 置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。

不同的机器运行结果有可能不同,是由于两个“并行”的线程抢夺处理器资源造成的。

线程属性:是否绑定、是否分离、堆栈地址、堆栈大小、优先级

默认的属性:非绑定、非分离、缺省 1M的堆栈、与父进程同样级别的优先级。

 

初始化属性

pthread_attr_init(&attr);     

初始化函数pthread_attr_init必须在pthread_create函数之前调用

绑定属性

pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);  

第二个参数可选为:PTHREAD_SCOPE_SYSTEM(绑定的)

PTHREAD_SCOPE_PROCESS(非绑定的)

建立线程

pthread_create(&tid, &attr, (void *) my_function, NULL);

分离属性

线程的分离状态决定了一个线程以什么样的方式来终止自己。

pthread_attr_setdetachstate(&attr, int detachstate)

第二个参数可选为:PTHREAD_CREATE_DETACHED(分离线程)

PTHREAD _CREATE_JOINABLE(非分离线程)。

非分离状态的线程,原有线程等待创建的线程结束,只有当pthread_join()函数返回时,创建的线程才算终止,才释放自己占用的系统资源。而分离状态的线程,没有被其他线程所等待,自己运行结束了,线程终止,马上释放系统资源。

如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是 在多线程编程里常用的方法。

线程优先级

存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。

记得头文件 sched.h

Eg

include <pthread.h>  

include <sched.h>  

pthread_attr_t attr;  

pthread_t tid;  

sched_param param;  

int newprio=20;  

 

pthread_attr_init(&attr);  

pthread_attr_getschedparam(&attr, &param);  

param.sched_priority=newprio;  

pthread_attr_setschedparam(&attr, &param);   

pthread_create(&tid, &attr, (void *)myfunction, myarg);

 

Mutex

mutex是一个互斥锁对象,互斥锁是为了防止多线程同时修改某一公共资源

 

线程的数据处理

线程数据(TSDThread-Specific Data)是多线程程序里,除了全局变量和局部变量外的第三者数据类型。

线程数据的特点:对线程外部的其他线程不可见;在线程内部,各个函数可以向使用全局变量一样调用它。

为每个线程数据创建一个键,在各个线程里,使用这个键来指代线程数据。

创建键的函数原型:

int pthread_key_create(pthread_key_t  *key, void(*_destr_function)(void *))

第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,若这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。

此函数常与pthread_once ((pthread_once_t *once_control, void (*init routine)(void)))一起使用,为了让这个键只被创建一次。

Eg

 

Pthread_key_t  myWinKey;

 

Void createWindow (void)

{

       F1_Window *win;

       Static pthread_once_t once = PTHREAD_ONCE_INIT;

 

       Pthread_once ( &once, createMyKey);

 

       Win = new F1_Window(0 , 0, 100, 100, “MyWindow”);

 

       setWindow( win );

 

       pthread_setpecific( MyWinKey, win);

}

 

Void createMyKey (void)

{

       Pthread_keycreate( &myWinKey, freeWinKey);

}

 

Void freeWinKey ( F1_Window * win)

{

       Delete win;

}

int pthread_setspecific (pthread_key_t key, const void *point)  

void * pthread_getspecific (pthread_key_t, key)

这两个函数将线程数据和一个键绑定在一起。

pthread_getspecific为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间,这个过程函数pthread_key_delete用来删除一个键,内存释放。但只释放键占用的内存,并不释放键关联的线程数据所占用的内存资源。

 

条件变量

通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,常与互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件的发生变化。

条件变量的结构为 pthread_cond_t 函数pthread_cond_init()被用来初始化一个条件变量

int pthread_cond_init( pthread_cond_t *cond, const pthread_condattr_t *cond_attr );

cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针,结构pthread_condattr_t是条件变量的属性结构,默认值是PTHREAD_PROCESS_PRIVATE

释放一个条件变量的函数为:pthread_cond_destroypthread_cond_t cond

pthread_cond_wait()使线程阻塞在一个条件变量上

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex )

线程的撤销

一个线程可以通过向另一个线程发生“请求”,来结束另一个线程的执行

pthread_cancel(pthread_t)

int pthread_setcancelstate( int state, int *oldtype)

state值为 PTHREAD_CANCEL_ENABLE

         PTHREAD_CANCEL_DISABLE

Int pthread_setcanceltype( int type, int *oldtype)

Type值可以为 PTHREAD_CANCEL_DEFERRED

             PTHREAD_CANCEL_ASYNCHRONOUS

Void pthread_testcancel(void)

 

使用GDB调试线程threadname(步骤)

编译:gcc –g threadname.c –o threadname –lpthread

开始调试程序:gdb threadname

设置断点:b main 或者 b **

执行程序:r

单步执行:n

查看系统线程:info thread

切换线程:thread 2

使程序继续运行: c

列车程序代码:list

显示变量内部数据:display mutex