先看一个多线程编程的实例,一般我们的程序只有一个线程,但是在许多大型的编程中,往往涉及到多线程编程的问题,例如Android操作系统里面对于复杂耗时的操作通常在其他线程中完成。
Linux系统下的多线程遵循POSIX标准。编写Linux下的线程需要包含头文件pthread.h,在生成可执行文件的时候需要链接库libpthread.a或者libpthread.so
Linux下线程创建函数pthread_create()。
在pthread_create()函数调用时,传入的参数有线程属性、线程函数、线程函数变量,用于生成一个某种特性的线程,线程中执行线程函数。创建线程使用函数pthread_create()
函数pthread_join()用来等待一个线程运行结束。这个函数是阻塞函数,一直到被等待的线程结束为止,函数才返回并且收回被等待线程的资源。
/* * pthread.c * 线程实例 */
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
static int run = 1; /*运行状态参数*/
static int retvalue ; /*线程返回值*/
void *start_routine(void *arg) /*线程处理函数*/
{
int *running = arg; /*获取运行状态指针*/
printf("子线程初始化完毕,传入参数为:%d\n",*running); /*打印信息*/
while(*running) /*当running控制参数有效*/
{
printf("子线程正在运行\n"); /*打印运行信息*/
usleep(1); /*等待*/
}
printf("子线程退出\n"); /*打印退出信息*/
retvalue = 8; /*设置退出值*/
pthread_exit( (void*)&retvalue); /*线程退出并设置退出值*/
}
int main(void)
{
pthread_t pt;
int ret = -1;
int times = 3;
int i = 0;
int *ret_join = NULL;
ret = pthread_create(&pt, NULL, (void*)start_routine, &run); /*建立线程*/
if(ret != 0) /*建立线程失败*/
{
printf("建立线程失败\n"); /*打印信息*/
return 1; /*返回*/
}
usleep(1); /*等待*/
for(;i<times;i++) /*进行3次打印*/
{
printf("主线程打印\n"); /*打印信息*/
usleep(1); /*等待*/
}
run = 0; /*设置线程退出控制值,让线程退出*/
pthread_join(pt,(void*)&ret_join); /*等待线程退出*/
printf("线程返回值为:%d\n",*ret_join); /*打印线程的退出值*/
return 0;
}
通常来说,建立一个线程的时候,使用默认属性就够了,但是很多时候需要调整线程的属性,特别是线程的优先级。
1.线程的属性结构
线程的属性结构为pthread_attr_t
typedef struct __pthread_attr_s
{
int __detachstate; /*线程的终止状态*/
int __schedpolicy; /*调度优先级*/
struct __sched_param __schedparam; /*参数*/
int __inheritsched; /*继承*/
int __scope; /*范围*/
size_t __guardsize; /*保证尺寸*/
int __stackaddr_set; /*运行栈*/
void *__stackaddr; /*线程运行栈地址*/
size_t __stacksize; /*线程运行栈大小*/
} pthread_attr_t;
2.线程的优先级
线程的优先级是经常设置的属性,由两个函数进行控制:pthread_attr_getschedparam()函数获得线程的优先级设置,函数pthread_attr_setschedparam()设置线程的优先级。
3.线程的绑定状态
设置线程绑定状态的函数为pthread_attr_setscope(),它有两个参数,第1个是指向属性结构的指针,第2个是绑定类型,它有两个取值:
PTHREAD_SCOPE_SYSTEM(绑定的)
PTHREAD_SCOPE_PROCESS(非绑定的)
4.线程的分离状态
线程的分离状态决定线程的终止方法。线程的分离状态有分离线程和非分离线程两种。pthread_attr_setdetachstate()函数
互斥锁是用来保护一段临界区的,它可以保证某时间段内只有一个线程在执行一段代码或者访问某个资源。
1.线程互斥的函数介绍
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); /*互斥初始化*/
int pthread_mutex_lock(pthread_mutex_t *mutex); /*锁定互斥*/
int pthread_mutex_trylock(pthread_mutex_t *mutex); /*互斥预锁定*/
int pthread_mutex_unlock(pthread_mutex_t *mutex); /*解锁互斥*/
int pthread_mutex_destroy(pthread_mutex_t *mutex); /*销毁互斥*/
2.线程互斥函数的例子
代码用线程互斥的方法构建了一个生产者和消费者的例子:建立了两个线程,函数producter_f()用于生产,函数consumer_f()用于消费。
/* * mutex.c * 线程实例 */
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
void *producter_f (void *arg); /*生产者*/
void *consumer_f (void *arg); /*消费者*/
int buffer_has_item=0; /*缓冲区计数值*/
pthread_mutex_t mutex; /*互斥区*/
int running =1 ; /*线程运行控制*/
int main (void)
{
pthread_t consumer_t; /*消费者线程参数*/
pthread_t producter_t; /*生产者线程参数*/
pthread_mutex_init (&mutex,NULL); /*初始化互斥*/
pthread_create(&producter_t, NULL,(void*)producter_f, NULL ); /*建立生产者线程*/
pthread_create(&consumer_t, NULL, (void *)consumer_f, NULL); /*建立消费者线程*/
usleep(1); /*等待,线程创建完毕*/
running =0; /*设置线程退出值*/
pthread_join(consumer_t,NULL); /*等待消费者线程退出*/
pthread_join(producter_t,NULL); /*等待生产者线程退出*/
pthread_mutex_destroy(&mutex); /*销毁互斥*/
return 0;
}
void *producter_f (void *arg) /*生产者线程程序*/
{
while(running) /*没有设置退出值*/
{
pthread_mutex_lock (&mutex); /*进入互斥区*/
buffer_has_item++; /*增加计数值*/
printf("生产,总数量:%d\n",buffer_has_item); /*打印信息*/
pthread_mutex_unlock(&mutex); /*离开互斥区*/
}
}
void *consumer_f(void *arg) /*消费者线程程序*/
{
while(running) /*没有设置退出值*/
{
pthread_mutex_lock(&mutex); /*进入互斥区*/
buffer_has_item--; /*减小计数值*/
printf("消费,总数量:%d\n",buffer_has_item); /*打印信息*/
pthread_mutex_unlock(&mutex); /*离开互斥区*/
}
}
线程的信号量与进程的信号量类似,但是使用线程的信号量可以高效地完成基于线程的资源计数。信号量实际上是一个非负的整数计数器,用来实现对公共资源的控制。
1.线程信号量初始化函数sem_init()
sem_init()函数用来初始化一个信号量
2.线程信号量增加函数sem_post()
sem_post()函数的作用是增加信号量的值,每次增加的值为1。当有线程等待这个信号量的时候,等待的线程将返回。
3.线程信号量等待函数sem_wait()
sem_wait()函数的作用是减少信号量的值,如果信号量的值为0,则线程会一直阻塞到信号量的值大于0为止。sem_wait()函数每次使信号量的值减少1,当信号量的值为0时不再减少。
4.线程信号量销毁函数sem_destroy()
sem_destroy()函数用来释放信号量sem
5.线程信号量的例子
/* * sem.c * 线程实例 */
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
void *producter_f (void *arg); /*生产者线程函数*/
void *consumer_f (void *arg); /*消费者线程函数*/
sem_t sem;
int running =1 ;
int main (void)
{
pthread_t consumer_t; /*消费者线程参数*/
pthread_t producter_t; /*生产者线程参数*/
sem_init (&sem, 0, 16); /*信号量初始化*/
pthread_create(&producter_t, NULL,(void*)producter_f, NULL ); /*建立生产者线程*/
pthread_create(&consumer_t, NULL, (void *)consumer_f, NULL); /*建立消费者线程*/
sleep(1); /*等待*/
running =0; /*设置线程退出*/
pthread_join(consumer_t,NULL); /*等待消费者线程退出*/
pthread_join(producter_t,NULL); /*等待生产者线程退出*/
sem_destroy(&sem); /*销毁信号量*/
return 0;
}
void *producter_f (void *arg) /*生产者处理程序代码*/
{
int semval=0; /*信号量的初始值为0*/
while(running) /*运行状态为可运行*/
{
usleep(1); /*等待*/
sem_post (&sem); /*信号量增加*/
sem_getvalue(&sem,&semval); /*获得信号量的值*/
printf("生产,总数量:%d\n",semval); /*打印信息*/
}
}
void *consumer_f(void *arg) /*消费者处理程序代码*/
{
int semval=0; /*信号量的初始值为0*/
while(running) /*运行状态为可运行*/
{
usleep(1); /*等待*/
sem_wait(&sem); /*等待信号量*/
sem_getvalue(&sem,&semval); /*获得信号量的值*/
printf("消费,总数量:%d\n",semval); /*打印信息*/
}
}