多线程编程
进程与线程
进程:
进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元;
进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。
从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。
"进程——资源分配的最小单位,线程——程序执行的最小单位“
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。
使用多线程的优点:
(1)和进程相比,它是一种非常“节俭”的多任务操作方式.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式.
运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间.据统计,一个进程的开销大约是一个线程开销的30倍左右。
(2)线程间方便的通信机制。
对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。
线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
(3)使多CPU系统更加有效.操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上.
(4)改善程序结构.一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改.
多线程函数:
1.pthread_create
函数的作用:创建一个线程
函数的原型:int pthread_create(pthread_t *thread,pthread_attr_t * attr, (void *)(*start_routine(void *),(void *)arg);
函数的参数:thread:线程的标识符
attr:线程的属性,一般设为NULL
start_routine:线程的执行函数
arg:传入到线程执行函数的参数
返 回 值:成功:0,出错:-1
头 文 件:#include <pthread.h>
2.pthread_exit
函数的作用:线程的退出
函数的原型:void pthread_exit(void * retval)
3.pthread_join
函数的作用:等待线程的退出
函数的原型:pthread_join(pthread_t th,void ** thread_return)
函数的参数:th:线程的标识符
thread_return:不为NULL时,存储线程结束时返回值
返 回 值:成功:0
出错:<0
举例:
创建多线程:thread_create.c
#include <stdio.h>
#include <pthread.h>
void *myThread1(void)
{
int i;
for (i=0; i<100; i++)
{
printf("This is the 1st pthread,created by zieckey.\n");
sleep(1);//Let this thread to sleep 1 second,and then continue to run
}
}
void *myThread2(void)
{
int i;
for (i=0; i<100; i++)
{
printf("This is the 2st pthread,created by zieckey.\n");
sleep(1);
}
}
int main()
{
int i=0, 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, 0);
pthread_join(id2, NULL);
return 0;
}
下面是有关线程与结构体得结合,大家可以在此基础上多做拓展~
thread_struct.c:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
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 argc,char *argv[])
{
pthread_t tidp;
int error;
struct menber *b;
b=(struct menber *)malloc( sizeof(struct menber) );
b->a = 4;
b->s = "zieckey";
error = pthread_create(&tidp, NULL, create, (void *)b);
if( error )
{
printf("phread is not created...\n");
return -1;
}
sleep(1);
printf("pthread is created...\n");
return 0;
}
注意:编译时需要加-lpthread
如:gcc thread_struct.c -lpthread
这样才会编译不出错!!
线程之间的同步和互斥:
进行多线程编程,因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂,通过下面这些技术的使用,可以解决
线程之间对资源的竞争:
1. 互斥量Mutex
2. 信号灯Semaphore
3. 条件变量Conditions
mutex互斥锁线程控制
· 互斥锁是用一种简单的加锁方法来控制对共享资源的原子操作。
· 互斥锁只有两种状态,也就是上锁和解锁,可以把互斥锁看作某种意义上的全局变量。
· 在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经被上锁的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。
互斥锁的基本操作:
互斥锁初始化:pthread_mutex_init()
互斥锁上锁:pthread_mutex_lock()
互斥锁判断上锁:pthread_mutex_trylock()
互斥锁接锁:pthread_mutex_unlock()
消除互斥锁:pthread_mutex_destroy()
4.pthread_mutex_init
函数的作用:初始化互斥锁
函数的原型:int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr)
函数的参数:mutex:互斥锁
mutexattr:PTHREAD_MUTEX_INITLALIZER:创建快速互斥锁
PTHREAD_RECURSIVE_MUTEX_INITLALIZER_NP:创建递归互斥锁
PTHREAD_ERRORCHECK_MUTEX_INITLALIZER_NP:创建检错互斥锁
返 回 值:成功0,出错:< 0
5.pthread_mutex_lock()
函数的作用:对互斥锁上锁,判断上锁,解除锁,破坏锁
函数的原型:pthread_mutex_lock(pthread_mutex_t * mutex);
pthread_mutex_trylock(pthread_mutex_t * mutex);
pthread_mutex_unlock(pthread_mutex_t * mutex);
pthread_mutex_deatroy(pthread_mutex_t * mutex);
返 回 值:成功 0, 出错:-1
举例:(大家可以通过例程进行更改,更加熟练掌握互斥锁)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_NUM 3
#define REPEAT_NUM 3
#define DELAY_TIME_LEVELS 6.0
pthread_mutex_t mutex;
void *thrd_func(void *arg)
{
int thrd_num = (int)arg;
int delay_time =0, count=0;
int res;
res = pthread_mutex_lock(&mutex);
printf("Thread %d is starting\n", thrd_num);
for(count=0; count<REPEAT_NUM; count++)
{
printf("\tThread %d:job %d delay=%d\n", thrd_num,count);
}
printf("Thread %d finish\n", thrd_num);
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main(void)
{
pthread_t thread[THREAD_NUM];
int no=0, res;
void *thrd_ret;
pthread_mutex_init(&mutex, NULL);
for(no=0; no<THREAD_NUM; no++)
{
res=pthread_create(&thread[no], NULL, thrd_func, (void *)no);
}
printf("creatr threads success\nwaiting for threads to finish....\n");
for(no=0; no<THREAD_NUM; no++)
{
res=pthread_join(thread[no], &thrd_ret);
}
pthread_mutex_destroy(&mutex);
return 0;
}
PV操作同步互斥:
6.sem_init
函数的作用:初始化信号量
函数的原型:int sem_init(sem_t *sem,int pshared,unsigned int value)
函数的参数:sem:信号量指针
pshared:0
value:信号量的初始值
返 回 值:成功 0,出错-1
头 文 件:#include <semaphore.h>
7.信号量的PV操作:
函数的原型:int sem_wait(sem_t *sem) --P操作
int sem_trywait(sem_t *sem)
int sem_post(sem_t *sem) --V操作
int sem_getvalue(sem_t *sem)
int sem_destroy(sem_t *sem)
函数的参数:sem :信号量指针
返 回 值:成功: 0,出错:-1
举一个非常简单的例子:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem1;
sem_t sem2;
void *mythread1(void)
{
int i = 0;
sem_wait(&sem1);
for(i = 0; i <2; i++)
{
printf("This is pthread 1!\n");
sleep(1);
}
sem_post(&sem2);
}
void *mythread2(void)
{
int i = 0;
sem_wait(&sem2);
for(i = 0; i < 2; i++)
{
printf("This is pthread 2!\n");
sleep(1);
}
sem_post(&sem1);
}
int main()
{
int i = 0;
int ret = 0;
pthread_t id1;
pthread_t id2;
sem_init(&sem1,0,1);
sem_init(&sem2,0,0);
ret = pthread_create(&id1,NULL,(void *)mythread1,NULL);
if(ret)
{
printf("Create thread1 error!\n");
return 1;
}
ret = pthread_create(&id2,NULL,(void *)mythread2,NULL);
if(ret)
{
printf("Create thread2 error!\n");
return 1;
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
sem_destroy(&sem1);
sem_destroy(&sem1);
return 0;
}