进程和线程的区别:
- 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。
- 进程本身不是基本运行单位,而是线程的容器。线程是操作系统能够进行运算调度的最小单位。
- 线程有自己的堆栈和局部变量,但没有单独的地址空间。
使用进程的优势:
和多进程相比,多线程是一种比较节省资源的多任务操作方式。启动一个新的进程必须分配给它独立的地址空间,每个进程都有自己的堆栈段和数据段,系统开销比较高,进行数据的传递只能通过进行间通信的方式进行。在同一个进程中,可以运行多个线程,运行于同一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享全局变量和对象,启动一个线程所消耗的资源比启动一个进程所消耗的资源要少。
1、创建线程
pthread_create函数来创建一个新的线程,函数声明:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数:
参数thread为指向线程ID的地址。
参数attr用于设置线程属性,NULL表示使用默认属性。
参数start_routine是线程运行函数的地址,填函数名。
参数arg是线程运行函数的参数。新创建的线程从start_routine函数的地址开始运行,该函数只有一个无类型指针参数arg。若要想向start_routine传递多个参数,可以将多个参数放在一个结构体中,然后把结构体的地址作为arg参数传入。
在编译时加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库。
2、线程的start_routine函数调用pthread_exit结束。
void pthread_exit(void *retval);
retval是一个无类型指针。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。
3、线程等待
int pthread_join(pthread_t thread, void **retval);
调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从例程中返回或者被取消。
调用pthread_join自动把线程置于分离状态,这样线程结束资源就释放。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
retval接收线程的退出状态,如果线程被取消,由retval指定的内存单元就置为PTHREAD_CANCELED。
#include <stdio.h>
#include <pthread.h>//线程头文件
//函数名即地址 指针即地址 pid相同 但是线程号不同
void* func(void* arg)
{
char* ret = "yang lang";
//传递数字 static int ret = 100;
printf("t1: arg:%d\n",*((int*)arg));
printf("t1:this is jc pid %d and xc pid %ld\n",getpid(),(unsigned long)pthread_self());
//void pthread_exit(void *retval);
//pthread_exit((void*)&ret);//整型数要取地址 传地址
pthread_exit((void*)ret); //指针就是地址
}
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
int main()
{
int data = 100;
int ret;
pthread_t t1;
char* pret = NULL;
ret = pthread_create(&t1,NULL,func,(void*)&data);
//地址、属性NULL、函数名地址、传参数
if(ret == -1)//返回-1 未创建成功
{
printf("creat failue!!\n");
}
printf("main:data%d\n",data);
printf("main:this is jc pid %d and xc pid %ld\n",getpid(),(unsigned long)pthread_self());
// int pthread_join(pthread_t thread, void **retval);
//调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从例程中返回或者被取消。
pthread_join(t1,(void**)&pret);
//整型数pthread_join(t1,(void**)&pret);printf("main:quit %d\n",*pret);
printf("main:quit %s\n",pret);
1,1 Top
//在同一片内存下 多个线程会争相抢夺资源 并改变g_data的值 编译跑起来---进程
#include <stdio.h>
#include <pthread.h>
int g_data = 0;
void *func1(int *arg) //线程1 打印传过来的值和自己pid 当g_data等于234时退出
{
printf("t1:%ld\n",(unsigned long)pthread_self());
printf("t1:%d\n",*arg);
while(1){
printf("t1:g_data = %d\n",g_data++);
sleep(1);
if(g_data == 2 || g_data == 3 || g_data ==4){
pthread_exit(NULL);
}
}
}
void *func2(int *arg)//线程2 打印传过来的值和自己的pid 循环打印gdata 让线程工作
{
printf("t2:%ld\n",(unsigned long)pthread_self());
printf("t2:%d\n",*arg);
while(1){
printf("t2:g_data = %d\n",g_data++);
sleep(1);
}
}
int main()
{
pthread_t t1;//定义线程1 2
pthread_t t2;
int param = 100;//要传的参数 整型数 字符串 结构体都可以
int ret; //存放 create的返回值 返回0创建成功 失败返回-1
ret = pthread_create(&t1,NULL,(void*)func1,(void*)¶m);//创建线程1
ret = pthread_create(&t2,NULL,(void*)func2,(void*)¶m);//创建线程2
if(ret == 0){ //创建成功
printf("main:pthread_create scuueed!!\n");
}
printf("main:%ld\n",(unsigned long)pthread_self());
while(1){ //主线程也不断打印g_data
printf("main:g_data = %d\n",g_data++);
sleep(1);
}
pthread_join(t1,NULL); //等待线程退出 否则程序会先结束
return 0;
}
线程互斥锁:
初始化锁
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
其中参数 mutexattr 用于指定锁的属性(见下),如果为NULL则使用缺省属性。
互斥锁的属性在创建锁的时候指定,当资源被某线程锁住的时候,其它的线程在试图加锁时表现将不同。当前有四个值可供选择:
PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。
PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。
PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,等待解锁后重新竞争。
阻塞加锁
int pthread_mutex_lock(pthread_mutex *mutex);
如果是锁是空闲状态,本线程将获得这个锁;如果锁已经被占据,本线程将排队等待,直到成功的获取锁。
解锁
int pthread_mutex_unlock(pthread_mutex *mutex);
销毁锁(此时锁必需unlock状态,否则返回EBUSY)
int pthread_mutex_destroy(pthread_mutex *mutex);
#include <stdio.h>
#include <pthread.h>
int g_data = 0;
pthread_mutex_t mutex;//全局变量锁
void* func1(void* arg)
{
//int pthread_mutex_lock(pthread_mutex *mutex);
pthread_mutex_lock(&mutex); //拿锁
printf("t1:%ld arg%d\n",(unsigned long)pthread_self(),*((int*)arg));
while(1){
printf("t1:g_data%d\n",g_data++);//g_data++ 到5得时候退出
sleep(1);
if(g_data == 5){
pthread_mutex_unlock(&mutex);//放回锁 期间其他拿不到锁只能等待
pthread_exit(NULL); //退出
}
}
}
void* func2(void* arg)
{
pthread_mutex_lock(&mutex);//拿锁
printf("t2:%ld arg%d\n",(unsigned long)pthread_self(),*((int*)arg));
while(1){
printf("t2:g_data%d\n",g_data++);
sleep(1);
}
pthread_mutex_unlock(&mutex);//放回锁
pthread_exit(NULL);
}
int main(){
int ret;
int data = 100;
int* pa = NULL;
pthread_t t1;
pthread_t t2;
//int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
pthread_mutex_init(&mutex,NULL);//初始化锁
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);i
ret = pthread_create(&t1,NULL,func1,(void*)&data);
if(ret == -1){
printf("main t1 create failue\n");
}
ret = pthread_create(&t2,NULL,func2,(void*)&data);
if(ret == -1){
printf("main t2 create failue\n");
}
while(1){
printf("main:g_data%d\n",g_data);
sleep(1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
//int pthread_mutex_destroy(pthread_mutex *mutex);
pthread_mutex_destroy(&mutex); //释放锁
return 0;
}
// 初始化锁 创建线程12 等待线程12 销毁锁
#include <stdio.h>
#include <pthread.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);//创建 指向线程、属性、函数、传参
//int pthread_join(pthread_t thread, void **retval);//等待 用二级指针存放一级指针数据str int
//void pthread_exit(void *retval); //退出线程
//初始化生成锁 ----先定义一个全局变量锁pthread_mutex_t mutex
//int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
//int pthread_mutex_lock(pthread_mutex *mutex);//拿锁
//int pthread_mutex_unlock(pthread_mutex *mutex);//放回锁
//int pthread_mutex_destroy(pthread_mutex *mutex);//销毁锁
static int g_data = 0;
pthread_mutex_t mutex;//全局变量锁
void* func1(int *arg)//线程1拿到锁++ 到g_data = 5的时候放回锁并退出
{
pthread_mutex_lock(&mutex);//加锁
printf("t1:pid%ld\n",(unsigned long)pthread_self());//打印线程pid
printf("t1:%d\n",*arg); //打印传过来的参数
while(1){
printf("t1:g_data=%d\n",g_data++);
sleep(1);
if(g_data == 5){
pthread_mutex_unlock(&mutex);//放回锁
break;
}
}
}
void* func2(int *arg)//线程2
{
pthread_mutex_lock(&mutex);;//拿锁 放锁 gdata++ 线程1拿到到5放回锁之后线程2才执行
printf("t2:pid%ld\n",(unsigned long)pthread_self());
printf("t2:%d\n",*arg);
pthread_mutex_unlock(&mutex);
while(1){
pthread_mutex_lock(&mutex);;
printf("t2:g_data=%d\n",g_data);
g_data++;
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_t t1;//unsigned long型 定义线程1 2
pthread_t t2;
int ret; //定义一个值存放 线程返回值
int param = 100; //传的参数 可以是整型数、字符串、结构体
pthread_mutex_init(&mutex,NULL);//初始化锁
ret = pthread_create(&t1,NULL,(void*)func1,(void*)¶m);//创建线程1
if(ret == 0){
printf("man:creat t1 succeed!\n");
}
ret = pthread_create(&t2,NULL,(void*)func2,(void*)¶m);//创建线程2
if(ret == 0){
printf("man:creat t2 succeed!\n");
}
printf("main:pid%ld\n",(unsigned long)pthread_self());
printf("main:g_data=%d\n",g_data);
pthread_join(t1,NULL);//等待线程1 退出
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);//销毁锁
return 0;
}
互斥锁死锁:
有两把锁,a线程拿到锁1后去拿锁二 b线程拿到锁2后去拿锁1,导致两个都拿不到锁,导致两个线程无法往下运行。
创建及销毁条件变量
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
触发
int pthread_cond_signal(pthread_cond_t &cond);
int pthread_cond_broadcast(pthread_cond_t cond);
这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程。
等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。
pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。
.销毁条件变量
int pthread_cond_destroy(pthread_cond_t cond);
int pthread_cond_destroy(pthread_cond_t cond);
#include <stdio.h>
#include <pthread.h>
int g_data = 0;
pthread_mutex_t mutex; //=PTHREAD_MUTEX_INITIALIZER;//静态初始化锁 主函数中不用初始化
pthread_cond_t cond; //=PTHREAD_COND_INITIALIZER;//静态初始化锁
void* func1(void* arg)
{
printf("t1:%ld %d\n",(unsigned long)pthread_self(),*((int*)arg));
while(1){
pthread_cond_wait(&cond,&mutex);//卡住等待
printf("t1:run---------%d\n",g_data);
g_data = 0;
sleep(1);//不需要退出 因为wait会卡住等待
}
pthread_exit(NULL);
}
void* func2(void* arg)线程1等待,线程2跑,数据加到5时触发线程1,线程1打印数值5
{
printf("t2:%ld %d\n",(unsigned long)pthread_self(),*((int*)arg));
while(1){
pthread_mutex_lock(&mutex);//上锁
printf("t2:g_data == %d\n",g_data++);
if(g_data == 5)
{
pthread_cond_signal(&cond);//5的时候触发 发送信号 开始执行func1
}
pthread_mutex_unlock(&mutex);//解开锁
sleep(1);
}
}
pthread_exit(NULL);
}
int main()
{
int data = 100;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex,NULL);//动态初始化
pthread_cond_init(&cond,NULL); //动态初始化
pthread_create(&t1,NULL,func1,(void*)&data);//创建线程1
pthread_create(&t2,NULL,func2,(void*)&data);//创建线程2
pthread_join(t1,NULL);//等待线程1、2退出
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);//销毁锁和条件
pthread_cond_destroy(&cond);
return 0;
}