文章目录
多进程linux命令
命令 | 作用 |
---|---|
ps -aux | 查看所有进程信息 |
ps -ajx | 查看所有进程信息(显示父进程id等更多信息) |
kill | 杀死进程 |
多进程编程
创建子进程
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int pid=fork();//创建子进程
if(pid<0)//子进程创建失败
perror("fork");//打印失败信息
if(pid>0)//子进程创建成功,且当前进入父进程执行,此时pid表示子进程id
{
printf( "father-> pid : %d, fpid : %d\n" , getpid() , getppid( ) );//getpid()表示获取当前进程id,getppid()表示获取当前进行的父进程id
sleep ( 1);//让父进程后死亡
}
if(pid==0)//此时进行子进程执行
printf( "child-> pid : %d , fpid:%d\n" , getpid() , getppid( ));
return 0;
}
- 父子进程的执行是随机的
- 子进程完全复制父进程用户区的数据,但与父进程的内核区的数据不完全相同,如进程id
多进程的GDB调试
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int args, char* argc[])
{
int pid = fork();
if (pid > 0)
{
printf("I am parentProcess,my id is %d\n", getpid());
for (int i = 0; i < 10; i++)
{
printf("%d -> i=%d\n",getpid(), i);
sleep(1);
}
}
if (pid == 0)
{
printf("I am childProcess,my id is %d and my parentProcess id is %d\n",
getpid(), getppid());
for (int j = 0; j < 10; j++)
{
printf("%d -> j=%d\n",getpid() ,j);
sleep(1);
}
}
else
perror("fork");
return 1;
}
创建三个子进程
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
for(int i=0;i<3;i++)
{
int pid=fork();
if(pid<0)
{
printf("error!\n");
return -1;
}
if(pid>0)
{
printf("father-> pid:%d,fpid:%d\n",getpid(),getppid());
// sleep(1);//让父进程后死亡
}
if(pid==0)
printf("child-> pid:%d,fpid:%d\n",getpid(),getppid());
}
sleep(10);
return 0;
}
- 在写操作下父子进程之间不共享全局变量,但在读操作下他们共享一份变量
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
int glo=99;//定义全局变量,在父进程中修改它,在子进程中打印它,看他是否出现变化
int main()
{
int pid=fork();
if(pid<0)
{
printf("error!\n");
return -1;
}
if(pid>0)
{
printf("father-> pid:%d,fpid:%d\n",getpid(),getppid());
printf("%p",&glo);//输出地址
glo++;
}
if(pid==0)
{
sleep(1);//让父进程先执行
printf("child-> pid:%d,fpid:%d\n",getpid(),getppid());
printf("%d\n",glo);
printf("%p",&glo);//输出地址
}
sleep(10);
return 0;
}
execl函数族
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
int execl(const char *path, const char *arg, ...);
execl()函数用于执行参数path字符串所代表的文件路径(必须指定路径),接下来是一系列可变参数,它们代表执行该文件时传递过去的 argv[0]、argv[1]… argv[n] ,最后一个参数必须用空指针NULL作为结束的标志。
#include<iostream>
using namespace std;
int main(int argc,char* argv[])//第一个参数为命令行的个数,第二个参数为命令行字符串,如ls -l,则argc=2,其中argv[0]="ls",argv[1]="-l"
{
cout<<"hello world"<<endl;
// for(int i=0;i<argc;i++)
// {
// cout<<"argc["<<i<<"]:"<<argv[i]<<endl;
// }
return 1;
}
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int pid = fork();
if (pid > 0)
{
printf("farent:%d\n", getpid());
}
if (pid == 0)
{
sleep(1);
//第一个参数为可执行文件名称,
//其余参数为该可执行文件传入的可变参数,最后一个参数必须为NULL
//当执行成功时不返回,否则返回-1
//作用:在进程中执行一个程序,替换原来的进程用户区数据
execl("/usr/bin/ls", "ls", "-n", NULL);//在子进程中执行系统程序ls -a
//如果execl函数执行成功,则子进程将不会继续向下执行
printf("child:%d\n", getpid());
}
for (int i = 0; i < 3; i++)
{
printf("%d\n", getpid());
}
return 0;
}
execlp函数:在环境变量中查找可执行文件,故在第一个参数中可不必写可执行文件的绝对路径
int ret=execlp("ls", "ls", "-n", NULL);//在子进程中执行系统程序ls -a
孤儿进程与僵尸进程
孤儿进程:父进程先死亡,子进程仍在运行,此时的子进程成为孤儿进程,且该子进程将由系统的初始进程init“领养”,故孤儿进程执行结束时可以被回收
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int args, char* argc[])
{
int pid = fork();
if (pid > 0)
{
printf("farent->%d\n", getpid());
for (int i = 0; i < 0; i++)
printf("i=%d\n", i);
}
if (pid == 0)
{
sleep(1);//让父进程执行完后死亡,留下子进程
printf("child->%d and my parent->%d\n", getpid(), getppid());
for (int j = 0; j < 3; j++)
{
printf("j=%d\n", j);
}
}
return 1;
}
僵尸进程:当进程结束时,其用户区数据部分将自动释放,但内核区的pcb必须由父进程释放,因此如果子进程执行结束时,父进程迟迟不回收(比如当父进程进入死循环时)子进程的pcb,则子进程此时成为僵尸进程,僵尸进程不能被kill命令杀死,且占用进程id号,然而系统的id号是有限的,因此如果存在大量的僵尸进程,将会给系统带来麻烦。处理僵尸进程的方法就是要求父进程必须回收子进程
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int args, char* argc[])
{
int pid = fork();
if (pid > 0)
{
while (1)//让父进程进入死循环,当子进程执行完时将无法回收子进程,则此时子进程将成功僵尸进程
{
printf("farent->%d\n", getpid());
sleep(1);
}
//for (int i = 0; i < 0; i++)
//printf("i=%d\n", i);
}
if (pid == 0)
{
//sleep(1);
printf("child->%d and my parent->%d\n", getpid(), getppid());
for (int j = 0; j < 3; j++)
{
printf("j=%d\n", j);
}
}
return 1;
}
进程的回收
int wait(int wstatus)*:阻塞父进程,直到有子进程退出时继续执行,以便让父进程回收子进程的资源
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
int pid;
for (int i = 0; i < 5; i++)//创建五个进程
{
pid = fork();
if (pid == 0)
{
break;
}
}
if (pid > 0)
{
while (1)
{
printf("farent -> %d\n", getpid());
//阻塞父进程的执行,
//直到一个子进程退出时就回收该子进程的资源
//如果回收成功就返回该子进程的id,否则(或子进程已全部回收完毕)返回-1
//该函数每次只能回收一个子进程
int ret = wait(NULL);
printf("child has died,id=%d\n", ret);
sleep(1);
}
}
if (pid == 0)
{
while (1)
{
printf("child -> %d\n", getpid());
sleep(1);
}
}
return 0;
}
int waitpid()
进程间通信
- 管道(简单,本质为内核缓冲区)
- 信号(系统开销小)
- 共享映射区(有无血缘关系的进程之间都可进行通信)
- 本地socket套接字(稳定)
匿名管道
父子进程管道通信
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2];//作为管道的传出参数,表示文件描述符,其中fd[0]表示读端,fd[1]表示写端
int ret = pipe(fd);//创建管道
int pid = fork();//创建子进程,此时父子进程共享管道两端的读写文件描述符
if (pid > 0)
{
//父进程关闭读端,写数据
//close(fd[0]);
char* str = "hello,I am farent!";
write(fd[1], str, strlen(str));//向管道的写端进行写数据
printf("farent -> %d,发送成功!\n", getpid());
char buf[64];
memset(&buf, 0, sizeof(buf));
int n0 = read(fd[0], buf, sizeof(buf));
printf("farent -> %d,共接受%d个字符:%s\n", getpid(), n0, buf);
}
if (pid == 0)
{
//close(fd[1]);
char buf[64];
memset(&buf, 0, sizeof(buf));
int n=read(fd[0], buf, sizeof(buf));
printf("child -> %d共接受到%d个字符:%s\n",getpid(), n, buf);
char* s = "I am child!";
write(fd[1], s, strlen(s));
printf("child -> %d发送成功!\n", getpid());
}
return 0;
}
兄弟进程管道通信
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
using namespace std;
int main()
{
int fd[2];
int ret=pipe(fd);
if(ret<0)
{
cout<<"pipe error!"<<endl;
return -1;
}
int i=-1;
for(i=0;i<2;i++)
{
int pid=fork();
if(pid<0)
{
cout<<"fork error!"<<endl;
return -1;
}
if(pid>0)
{
// cout<<"fater:"<<getpid()<<endl;
wait(NULL);
}
else
{
cout<<"i=="<<i<<"\tchild:"<<getpid()<<"--->"<<getppid()<<endl;
break;
}
}
if(i==0)//子进程1
{
close(fd[0]);//关闭读端,写入数据
char message[64]="hello world!";
write(fd[1],message,strlen(message));
}
if(i==1)//子进程2
{
close(fd[1]);//关闭写端,接受数据
char buff[64];
memset(&buff,0,sizeof(buff));
int n=read(fd[0],buff,sizeof(buff));
cout<<"共受到"<<n<<"个字符\t--->\t"<<buff<<endl;
}
return 1;
}
用管道通信模拟系统命令ps aux | grep bash
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
int fd[2];
int ret = pipe(fd);//创建管道
if (ret < 0)
{
perror("pipe");
return 0;
}
int pid = fork();//创建子进程
if (pid > 0)//父进程执行ps -aux,并将数据通过管道发送给子进程
{
close(fd[0]);//关闭读端
dup2(fd[1], STDOUT_FILENO);//由于execlp函数的输出为std屏幕,因此需要将输出重定向到管道的写端
execlp("ps", "ps", "-aux",NULL);//执行ps -aux
perror("execlp");//如果excelp函数执行成功,则该句不执行,否则可通过该语句返回错误信息
wait(NULL);//等待子进程结束回收子进程
return 0;
}
if (pid == 0)//子进程接受ps -aux信息
{
close(fd[1]);//关闭写端
char buf[1024];//定义缓冲区接受数据
memset(&buf, 0, sizeof(buf));
while (read(fd[0], buf, sizeof(buf)) > 0)//由于缓冲区大小有限,因此一次读取的内容不完整,需要进行循环读取,直到将管道数据读取完毕
{
printf("%s", buf);
memset(&buf, 0, 1024);
}
}
else {
perror("fork");
return 0;
}
return 0;
}
有名管道
有名管道的使用
可实现非血缘关系进程之间的通信
write.cpp
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int ret = access("fifo", F_OK);//判断文件是否存在
if (ret < 0)
{
printf("管道不存在,创建管道\n");
ret = mkfifo("fifo", 0777);//创建有名管道文件
if (ret == -1)
{
perror("mkfifo");
return 0;
}
}
//打开管道文件
int fd = open("fifo", O_WRONLY);//以只写方式打开文件
if (fd == -1)
{
perror("open");
return 0;
}
//向管道文件中写数据
for (int i = 0; i < 100; i++)
{
char buf[1024];
memset(&buf, 0, sizeof(buf));
sprintf(buf, "hello,%d\n", i);
printf("发送数据:%s\n", buf);
write(fd, buf, strlen(buf));
sleep(1);
}
close(fd);
return 0;
}
read.cpp
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
int main()
{
int fd = open("fifo", O_RDONLY);//打开管道文件
if (fd < 0)
{
perror("open");
exit(0);
}
while (1)
{
char buf[1024];
memset(&buf, 0, sizeof(buf));
int len = read(fd, buf,sizeof(buf));//读取管道文件数据
if (len == 0)
{
printf("写端关闭了\n");
break;
}
printf("接受数据:%s\n", buf);
}
close(fd);
return 0;
}
利用有名管道实现聊天功能
线程
线程创建
//int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
// void* (*start_routine) (void*), void* arg);
//参数
// -线程号
// -线程属性
// -函数指针,其内容即是子线程的具体函数代码
// 子线程的返回类型为void*,传入参数类型也为void*
// -给第三个参数(子线程函数)的传入参数
//返回值
// 创建成功则返回0,否则返回错误号,通过streero返回错误信息
#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
void* callback(void* arg)
{
printf("child thread....\npid=%d,tid2=%d\nnum=%d\n",
getpid(),pthread_self(),*(int*)arg);//将空指针参数arg转换为int*,再解引用取值
return NULL;
}
int main()
{
pthread_t tid;
//创建子线程,子线程具体代码由callback函数执行,并通过tid传出子线程的id
//int ret = pthread_create(&tid, NULL, callback, NULL);
//设置子线程传入参数
int num = 10;
int ret = pthread_create(&tid, NULL, callback, (void*)&num);//将传入参数地址并转换类型为void*
if (ret != 0)
{
char* str = strerror(ret);
printf("error:%s\n", str);
return 0;
}
//main()为主线程
sleep(1);
printf("tid1=%d", tid);
for (int i = 0; i < 3; i++)
{
printf("%d\n", i);
}
return 0;
}
线程传参
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<iostream>
using namespace std;
//线程执行函数
struct test
{
int data;
// char name[64];
string name;
};
void *mythread(void* arg)
{
//获取子线程所在的进程空间id与其自身进程号
printf("child thread,pid==[%d],id==[%ld]\n",getpid(),pthread_self());
// int n=*(int *)arg;
// printf("n=%d\n",n);
struct test *t;
t=(struct test*)arg;
cout<<t->data<<"\t"<<t->name<<endl;
}
int main()
{
int n=99;//传入整型
struct test t;//传入结构体
t.data=10;
t.name="LiHua";
pthread_t thread;
// int ret=pthread_create(&thread,NULL,mythread,NULL);
int ret =pthread_create(&thread,NULL,mythread,&t);
if(ret!=0)
{
// printf("pthread_create error,[%s]\n",strerror(ret));
cout<<"pthread_create error"<<ret<<endl;
return -1;
}
printf("main thread,pid==[%d],id==[%ld]\n",getpid(),pthread_self());
sleep(1);//由于子线程与主线程共享地址空间,故当主线程空间撤销时,子线程也将不存在,该语句是为了防止子线程还未启动但主线程却撤销
return 0;
}
多线程创建
#include<iostream>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
using namespace std;
typedef struct person
{
string name;
int age;
}person;
void* mythread(void* arg)
{
// person *p;
// p=(person* )arg;
// cout<<"name:"<<p->name<<endl;
// cout<<"age:"<<p->age<<endl;
int n=*(int*)arg;
cout<<"\t"<<n<<"->"<<getpid()<<" "<<pthread_self()<<endl;
}
int main()
{
// person per;
// per.name="李华";
// per.age=18;
pthread_t thread[5];
int arr[5];
for(int i=0;i<5;i++)
{
arr[i]=i;
int ret =pthread_create(&thread[i],NULL,mythread,&arr[i]);
if(ret!=0)
{
cout<<"pthread_create error!"<<endl;
return -1;
}
}
cout<<getpid()<<"\t-->\t"<<pthread_self()<<endl;
sleep(1);
return 1;
}
线程分离
int pthread_detach(pthread_t thread);
/*
功能 使调用线程与当前进程分离,分离后不代表此线程不依赖当前进程,线程分离的目的是将线程资源回收的工作交由系统自动完成,也就是说当被分离的线程 结束之后,系统会自动回收它的资源,不需要使用pthread_join进行回收,所以,此函数不会阻塞
参数 thread 线程号
返回 成功 0 失败 非0
*/
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
void* mythread(void* arg)
{
int n = *(int*)arg;
printf("child%d:-> pid=%d,tid=%d\n", n, getpid(), pthread_self());
//sleep(3);
return NULL;
}
int main()
{
pthread_t tid;
int num = 10;
int ret = pthread_create(&tid, NULL, mythread, &num);
if (ret != 0)
{
char* error = strerror(ret);
printf("error:%s\n", error);
return -1;
}
//sleep(1);
//设置子线程为分离状态,运行结束后可有系统自动回收其资源
pthread_detach(tid);
pthread_exit(NULL);//退出当前线程 return 0;
}
线程回收
1. 用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。
2. 对线程的资源进行回收:如果一个线程是非分离的(默认情况下创建的线程都是非分离)并且没有对该线程使用 pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int tt = 10;
void* mythread(void* arg)
{
int n = *(int*)arg;
printf("child%d:-> pid=%d,tid=%d\n", n, getpid(), pthread_self());
//sleep(3);
//return NULL;
pthread_exit ((void*)&tt);//return (void*)&tt
}
//int pthread_join(pthread_t thread, void** retval);
//作用:连接已经终止的线程,回收子线程资源。阻塞当前线程执行,直到要回收的线程运行结束用当前线程回收子线程
//注:子进程只能通过父进程回收,而线程的资源可通过其他线程回收,一般情况下我们指定用主线程进行回收
//参数:
// -要回收的线程id
// -传出参数,将回收线程的返回值传出来,其类型为二级指针,即指针变量的地址
//不能连接已经分离的线程
int main()
{
pthread_t tid;
int num = 10;
int ret = pthread_create(&tid, NULL, mythread, &num);
if (ret != 0)
{
char* error = strerror(ret);
printf("error:%s\n", error);
return -1;
}
//sleep(1);
//pthread_join(tid, NULL);//回收子线程
int* thread_reval;//用指针变量接收传出参数值
pthread_join(tid, (void**)&thread_reval);//回收子线程,并传出子线程返回值
printf("线程资源回收完毕!\n");
printf("tt=%d\n", *thread_reval);
pthread_exit(NULL);//退出当前线程 return 0;
}
线程属性
初始化线程属性变量 pthread_attr_init()
设置/获取线程属性
撤销线程属性变量 pthread_attr_destory()
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
void* mythread(void* arg)
{
printf("childThreadId:%d\n", pthread_self());
return NULL;
}
int main()
{
pthread_attr_t attr;
pthread_attr_init(&attr);//初始化线程变量
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置属性变量为分离状态
pthread_t tid;
int ret= pthread_create(&tid,&attr,mythread,NULL);
if (ret != 0)
{
char* error = strerror(ret);
printf("error:%s\n", error);
return -1;
}
size_t size;
pthread_attr_getstacksize(&attr, &size);//获取线程默认分配栈的大小
printf("stackSize:%ld\n", size);
sleep(1);
pthread_attr_destroy(&attr);//销毁线程属性变量
return 0;
}
线程同步
同步概念与测试
测试:设置三个线程进行售票,其中总票数不变,设置为全局变量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int stick = 100;//总票数
void* mythread(void* arg)
{
//临界区,各线程并发执行
while (stick > 0)
{
usleep(3000);
printf("%d:正在售出第%d张票\n", pthread_self(), stick);
stick--;
}
return NULL;
}
int main()
{
pthread_t tid[3];
//创建三个子线程进行售票
for (int i = 0; i < 3; i++)
{
int ret = pthread_create(&tid[i], NULL, mythread, NULL);
if (ret != 0)
{
char* error = strerror(ret);
printf("error:%s\n", error);
return -1;
}
}
//回收子线程资源
for (int i = 0; i < 3; i++)
{
pthread_join(tid[i],NULL);
}
return 0;
}
互斥锁
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int stick = 100;//总票数
//定义互斥锁,该锁一般定义为全局变量,否则如果定义在主线程中,当主线程结束之后回收栈资源,则该锁将不能被再次使用
pthread_mutex_t mutex;
void* mythread(void* arg)
{
//临界区,各线程并发执行
while (1)
{
pthread_mutex_lock(&mutex);//加锁
if (stick > 0)
{
usleep(3000);
printf("%d:正在售出第%d张票\n", pthread_self(), stick);
stick--;
}
else {
pthread_mutex_unlock(&mutex);//解锁
break;
}
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t tid[3];
pthread_mutex_init(&mutex,NULL);//初始化互斥锁
//创建三个子线程进行售票
for (int i = 0; i < 3; i++)
{
int ret = pthread_create(&tid[i], NULL, mythread, NULL);
if (ret != 0)
{
char* error = strerror(ret);
printf("error:%s\n", error);
return -1;
}
}
//回收子线程资源
for (int i = 0; i < 3; i++)
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mutex);//释放互斥锁资源
return 0;
}
#include<iostream>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
using namespace std;
pthread_mutex_t mutex;//定义锁
void* mythread(void* arg)
{
int n;
int c=*(int*)arg;
for(int i=0;i<5;i++)
{
if(c==0)
{
pthread_mutex_lock(&mutex);//第一个线程加锁
cout<<"hello ";
sleep(rand()%3);
cout<<"world!"<<endl;
pthread_mutex_unlock(&mutex);//第一个线程解锁
}
else
{
pthread_mutex_lock(&mutex);//第二个线程加锁
cout<<"HELLO ";
sleep(rand()%3);
cout<<"WORLD!"<<endl;
pthread_mutex_unlock(&mutex);//第二个线程解锁
}
}
}
int main()
{
srand((unsigned)time(0));
pthread_mutex_init(&mutex,NULL);//初始化锁
pthread_t thread[2];
int arr[2];
for(int i=0;i<2;i++)
{
arr[i]=i;
int ret=pthread_create(&thread[i],NULL,mythread,&arr[i]);
if(ret!=0)
{
cout<<"第"<<i<<"个线程error!"<<endl;
return -1;
}
}
pthread_join(thread[0],NULL);
pthread_join(thread[1],NULL);
pthread_mutex_destroy(&mutex);//释放锁
return 1;
}
加锁前:
加锁后:
死锁
读写锁
写独占,读共享
#include<iostream>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
using namespace std;
int number=0;
pthread_rwlock_t rwlock;//定义读写锁
void* thread_read(void* arg)
{
int c=*(int*)arg;
int n;
while(1)
{
pthread_rwlock_rdlock(&rwlock);//加读锁
n=number;
cout<<"r:["<<c<<"]"<<n<<endl;
pthread_rwlock_unlock(&rwlock);//解锁
sleep(1);
}
}
void* thread_write(void* arg)
{
int c=*(int*)arg;
int n;
while(1)
{
pthread_rwlock_wrlock(&rwlock);//加写锁
n=number;
n++;
number=n;
cout<<"w:["<<c<<"]"<<number<<endl;
pthread_rwlock_unlock(&rwlock);//解锁
sleep(1);
}
}
int main()
{
pthread_rwlock_init(&rwlock,NULL);//读写锁初始化
int arr[8];
pthread_t thread[8];
int i;
for(int i=0;i<3;i++)//定义三个写进程
{
arr[i]=i;
int ret=pthread_create(&thread[i],NULL,thread_write,&arr[i]);
if(ret!=0)
{
cout<<"thread_read error!"<<endl;
return -1;
}
}
for(int i=3;i<8;i++)//定义五个读进程
{
arr[i]=i;
int ret=pthread_create(&thread[i],NULL,thread_read,&arr[i]);
if(ret!=0)
{
cout<<"thread_write error!"<<endl;
return -1;
}
}
for(int j=0;j<8;j++)
{
pthread_join(thread[j],NULL);
}
pthread_rwlock_destroy(&rwlock);//释放读写锁
return 1;
}
加锁前:
加锁后:
条件变量
单生产者消费者线程
#include<iostream>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<time.h>
#include<pthread.h>
using namespace std;
//定义链表结构
typedef struct node
{
int data;
struct node* next;
}node,*list;
//维护一个共享链表
list l=NULL;
#if 0
l=new node;
l->next=NULL;
#endif
//定义互斥变量与条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
//生产者进程
void* producer(void* arg)
{
// list l=(node*)arg;
srand((unsigned)time(0));
while(1)
{
//生产产品
node* s=new node;
s->data=rand()%100+1;
cout<<"生产中:"<<s->data<<endl;
//将产品放入共享数据区域
pthread_mutex_lock(&mutex);//加锁
#if 0
s->next=l->next;
l->next=s;
#endif
//头插
#if 1
s->next=l;
l=s;
#endif
pthread_mutex_unlock(&mutex);//解锁
//发信号取数据,若有多个等待被唤醒的线程,则该函数可一次性唤醒所有等待唤醒的线程,这些之前等待的线程将自行抢占CPU
pthread_cond_signal(&cond);
sleep(rand()%3);
}
}
//消费者进程
void* consumer(void* arg)
{
srand((unsigned)time(0));
// list l=(node*)arg;
while(1)
{
//从共享数据区域中取数据
pthread_mutex_lock(&mutex);//加锁
if(l==NULL)
{
//等待生产者进程唤醒该进程
pthread_cond_wait(&cond,&mutex);
}
//取产品
// node* p=l->next;
node* p=l;
cout<<"已消费:"<<p->data<<endl;
// l->next=p->next;
l=l->next;
delete p;
pthread_mutex_unlock(&mutex);//解锁
sleep(rand()%3);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_t threadProducer;
pthread_t threadConsumer;
//维护一个共享链表
// list l=(node*)malloc(sizeof(node));
// list l=new node;
// l->next=NULL;
pthread_create(&threadProducer,NULL,producer,&l);
pthread_create(&threadConsumer,NULL,consumer,&l);
pthread_join(threadProducer,NULL);
pthread_join(threadConsumer,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 1;
}
注:pthread_cond_wait()函数必须与mutex互斥变量配对使用,因为其内部实现的主要步骤为:
1.线程放在等待队列上,解锁
2.等待 pthread_cond_signal或者pthread_cond_broadcast信号之后去竞争锁
3.若竞争到互斥索则加锁。
多生产者消费者线程
#include<iostream>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<time.h>
#include<pthread.h>
using namespace std;
//定义链表节点结构
typedef struct node
{
int data;
struct node* next;
}node,*list;
//定义共享数据区域
list l=NULL;
#if 0
l=new node;
l->next=NULL;
#endif
//定义互斥变量与条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
//生产者线程
void* producer(void* arg)
{
int n=*(int*)arg;
srand((unsigned)time(0));
while(1)
{
//生产数据
node* s=new node;
s->data=rand()%100+1;
cout<<"["<<n<<"]生产:"<<s->data<<endl;
//互斥将生产的数据放入共享缓冲区
pthread_mutex_lock(&mutex);//加锁
#if 0
s->next=l->next;
l->next=s;
#endif
//头插法将数据插入到缓冲区中
#if 1
s->next=l;
l=s;
#endif
pthread_mutex_unlock(&mutex);//解锁
pthread_cond_signal(&cond);//唤醒消费者进程
sleep(rand()%3);
}
}
//消费者线程
void* consumer(void* arg)
{
int n=*(int*)arg;
srand((unsigned)time(0));
// list l=(node*)arg;
while(1)
{
pthread_mutex_lock(&mutex);
if(l==NULL)
{
pthread_cond_wait(&cond,&mutex);//等待唤醒
}
//由于pthread_cond_singal()唤醒的所有的消费者进程,使得各消费者进行抢占CPU,因此会出现一个消费者消费完所有数据后此后还有消费者取数据,而此时l==NULL,故p->data中已无数据
if(l==NULL)
{
pthread_mutex_unlock(&mutex);//当出现缓冲区中数据无数据进行判断时,首先需要对缓冲区进行解锁以防止出现死锁现象
continue;//跳出该消费者取数据循环中
}
// node* p=l->next;
node* p=l;
cout<<"["<<n<<"]消费:"<<p->data<<endl;
// l->next=p->next;
l=l->next;
delete p;
pthread_mutex_unlock(&mutex);
sleep(rand()%3);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
//定义五个生产者与消费者进程组
pthread_t threadProducer[5];
pthread_t threadConsumer[5];
int arr[5];
for(int i=0;i<5;i++)
{
arr[i]=i;
pthread_create(&threadProducer[i],NULL,producer,&arr[i]);
pthread_create(&threadConsumer[i],NULL,consumer,&arr[i]);
}
for(int j=0;j<5;j++)
{
pthread_join(threadProducer[j],NULL);
pthread_join(threadConsumer[j],NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 1;
}
信号量
#include<iostream>
#include<string>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<time.h>
using namespace std;
//定义链表节点结构
typedef struct node
{
int data;
struct node* next;
}node,*list;
//定义共享区域
list l=NULL;
//定义信号量
sem_t pro;
sem_t con;
//生产者线程
void* producer(void* arg)
{
srand((unsigned)time(0));
while(1)
{
sem_wait(&pro);
//生产数据
node* s=new node;
s->data=rand()%100+1;
//将数据存放共享区域
s->next=l;
l=s;
cout<<"producer:"<<s->data<<endl;
sem_post(&con);//给消费者发信号数据已经存放完毕
sleep(rand()%3);
}
}
//消费者线程
void* consumer(void* arg)
{
srand((unsigned)time(0));
while(1)
{
sem_wait(&con);
//从共享区域中读出数据
node* p=l;
cout<<"consumer:"<<p->data<<endl;
//将读出的数据从共享区域中删除
l=p->next;
delete p;
sem_post(&pro);//给生产者发信号数据已从共享区域中读出
sleep(rand()%3);
}
}
int main()
{
//初始化信号量
sem_init(&pro,0,5);
sem_init(&con,0,0);
//设置线程id
pthread_t thread_producer;
pthread_t thread_consumer;
//创建子线程
pthread_create(&thread_producer,NULL,producer,NULL);
pthread_create(&thread_consumer,NULL,consumer,NULL);
//等待子线程退出,并回收子线程区域
pthread_join(thread_producer,NULL);
pthread_join(thread_consumer,NULL);
//销毁子线程信号量
sem_destroy(&pro);
sem_destroy(&con);
return 1;
}