目录
线程和进程的区别:
1.进程是资源分配的最小单位,线程是资源调度的最小单位。
2.进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段,堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此切换一个线程的花费远比进程要小得多,同时创建一个线程的开销也比进程要小的多。
3.线程之间的通信方式更加方便,同一进程下的线程共享全局变量,静态变量等数据。而进程之间的通信方式只能以IPC的方式进行。
4.多进程程序更加健壮,多线程程序只要有一个线程死掉,整个进程也死掉了。而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己的独立地址空间。
线程的创建:
函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数说明:
1.参数thread指向存放新创建线程的线程ID的地址
2.attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。
3.start_routine是个函数指针,该函数返回类型是void*,同时形式参数也是void*。新创建的线程从start_routine函数的地址开始运行。该函数只有一个无类型指针参数arg.如果需要向start_routine函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
线程退出:
函数原型:
void pthread_exit(void *retval);
参数说明:
retval是一个无类型指针,进程中的其他线程可以通过调用pthread——join函数访问到这个指针。
线程等待:
函数原型:
int pthread_join(pthread_t thread, void **retval);
参数说明:
调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit. 如果对线程的返回值不感兴趣,可以把retval置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获得线程的终止状态。
例子:
#include<stdio.h>
#include<pthread.h>
/*
线程等待函数 int pthread_join(pthread_t thread, void **retval);
线程退出函数 void pthread_exit(void *retval);
线程创建函数 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
*/
void *func1(void *arg)
{
static int retval = 1; //将退出状态存放在retval,使用static修饰该变量否则函数返回后该变量就释放了
printf("param = %d\n", *((int *)arg));
printf("func1_ID:%ld\n",pthread_self());
pthread_exit((void *)&retval);
}
int main(void)
{
pthread_t t1;
int param = 10;
int *retval = NULL;
int ret;
/*创建func1线程*/
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if(ret == 0)
{
printf("create func1 pthread successfully\n");
}
printf("main_ID:%ld\n",pthread_self());
pthread_join(t1,(void *)&retval);
printf("retval = %d\n",*retval);
return 0;
}
互斥锁相关API
1.互拆量(mutex)从本质上来说是一把锁,在访问共享资源前对互拆量进行加锁,在访问完成后释放互拆量上的锁。
2.对互拆量进行加锁后,任何其他试图再次对互拆量加锁的线程将会被阻塞直到当前线程释放该互拆锁。
3.如果释放互拆锁时有多个线程阻塞,所有在该互拆锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互拆量加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。
创建互斥锁:
函数原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数说明:
1.在使用互斥锁前,需要定义互斥锁(全局变量),定义互斥锁对象形式为:pthread_mutex_t lock;
2.mutex 是个指针,指向需要初始化的互斥锁;
3.参数attr指定了新建互斥锁的属性。如果参数attr为NULL,则使用默认的互斥锁属性,默认属性为快速互斥锁 。
销毁互斥锁:
函数原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数说明:
mutex指定了需要销毁的互斥锁;
加上互斥锁和解除互斥锁:
函数原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数说明:
mutex指定了需要加锁和解锁的互斥锁;
例子:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
/*互斥锁*/
pthread_mutex_t lock;
void *func1(void *arg)
{
/*加锁*/
pthread_mutex_lock(&lock);
for(int i = 0; i < 10; i++)
{
printf("this is func1\n");
sleep(1);
}
/*解锁*/
pthread_mutex_unlock(&lock);
}
void *func2(void *arg)
{
/*加锁*/
pthread_mutex_lock(&lock);
for(int i = 0; i < 10; i++)
{
printf("this is func2\n");
sleep(1);
}
/*解锁*/
pthread_mutex_unlock(&lock);
}
int main(void)
{
pthread_t t1;
pthread_t t2;
/*初始化互斥锁*/
pthread_mutex_init(&lock,NULL);
/*创建func1线程*/
pthread_create(&t1, NULL, func1,NULL);
/*创建func2线程*/
pthread_create(&t2, NULL, func2,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
/*销毁锁*/
pthread_mutex_destroy(&lock);
return 0;
}
死锁:
由于互斥锁使用不当,导致多个进程无法进行下一步代码运行。
死锁是如何造成的?
造成死锁最少需要两把锁,线程A已经获得了其中一把锁,还想去获得另外一把锁;线程B已经获得了线程A想要得那把锁,同时又想去获得线程A获得的那把锁。两个进程都在等待对方释放锁,这种互相等待的情况就称之为死锁。
死锁例子:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
/*互斥锁*/
pthread_mutex_t lock;
pthread_mutex_t lock1;
void *func1(void *arg)
{
pthread_mutex_lock(&lock);//获得了锁lock
sleep(1);//进程睡眠1s
pthread_mutex_lock(&lock1);//想获得锁lock1,但是此时lock1已经被func2线程拿到了
for(int i = 0; i < 10; i++)
{
printf("this is func1\n");
sleep(1);
}
/*解锁*/
pthread_mutex_unlock(&lock);
}
void *func2(void *arg)
{
pthread_mutex_lock(&lock1);//获得了锁lock1
sleep(1);//进程睡眠1s
pthread_mutex_lock(&lock);//想获得锁lock,但是此时lock已经被func1线程拿到了
for(int i = 0; i < 10; i++)
{
printf("this is func2\n");
sleep(1);
}
/*解锁*/
pthread_mutex_unlock(&lock1);
}
int main(void)
{
pthread_t t1;
pthread_t t2;
/*初始化互斥锁*/
pthread_mutex_init(&lock,NULL);
pthread_mutex_init(&lock1,NULL);
/*创建func1线程*/
pthread_create(&t1, NULL, func1,NULL);
/*创建func2线程*/
pthread_create(&t2, NULL, func2,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
/*销毁锁*/
pthread_mutex_destroy(&lock);
pthread_mutex_destroy(&lock1);
return 0;
}
条件变量:
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
函数原型:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);----初始化条件变量
int pthread_cond_destroy(pthread_cond_t *cond);----销毁一个条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) ----线程等待条件变量的成立
int pthread_cond_signal(pthread_cond_t *__cond);----激活一个等待该条件变量的线程
int pthread_cond_broadcast(pthread_cond_t *cond);----激活所有等待该条件变量的线程
在使用条件变量,需要定义条件变量(全局变量),定义条件变量对象形式为:pthread_cond_t cond;
例子:
当全局变量g_data为3时候,t1线程发送信号激活线程t2。
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
int g_data;
/*互斥锁*/
pthread_mutex_t lock;
/*条件变量*/
pthread_cond_t cond;
void *func1(void *arg)
{
printf("func1 ID:%ld\n",pthread_self());
while(1)
{
printf("fun1:g_data = %d\n",g_data++);
sleep(1);
if(g_data == 3)
{
pthread_cond_signal(&cond);
}
}
/*线程退出*/
pthread_exit(NULL);
}
void *func2(void *arg)
{
printf("func2 ID:%ld\n",pthread_self());
/*线程等待*/
pthread_cond_wait(&cond,&lock);
while(1)
{
printf("fun2:g_data = %d\n",g_data++);
sleep(1);
}
/*线程退出*/
pthread_exit(NULL);
}
int main(void)
{
pthread_t t1;
pthread_t t2;
/*初始化互斥量*/
pthread_mutex_init(&lock,NULL);
/*初始化条件变量*/
pthread_cond_init(&cond,NULL);
/*线程的创建*/
pthread_create(&t1,NULL,func1,NULL);
pthread_create(&t2,NULL,func2,NULL);
/*线程等待*/
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}