线程的引入:
由于进程的地址空间是私有的,在进行切换的时候,系统开销较大,为了解决这个问题,提高系统的性能,引入轻量级别进程的概念,将这种轻量级别的进程称为线程。
进程和线程的区别:
进程是系统资源分配的基本单位
线程是系统任务调度的基本单位
线程的资源
1、一个进程可以有多个线程,共享以下资源
(1)静态数据
(2)进程打开的文件描述符
(3)信号处理函数
(4)当前路径
(5)可执行指令
(6)用户ID和用户组ID
复制代码
2、每一个线程私有资源
(1)线程ID
(2)程序计数器和相关寄存器
(3)线程任务函数里面的局部变量
(4)本身错误号:errno
(5)信息掩码
(6)线程本身的执行状态和线程属性
复制代码
二、线程API函数
注意:线程相关函数不属于标准C库,编译多线程代码的时候,需要链接线程库pthread (gcc 1.c -o 1 -lpthread)
(1)线程创建
线程例程函数需要自己定义,函数接口如下:
void *routine(void *arg)
{
}
复制代码
(二)结合指定线程
1,如果线程退出时没有退出值,那么retval可以指定为 NULL。
2,pthread_join()指定的线程如果尚在运行,那么他将会阻塞等待。
3,pthread_tryjoin_np()指定的线程如果尚在运行,那么他将会立即出错返回。
复制代码
(三)线程退出
注意:局部变量在函数退出的时候,会释放,如果线程函数需要返回数据,就必须要保证这个数据退出函数依然有效,所以我们建议,要么用静态数据,要么用堆空间
pthread_exit()退出线程,只会退出当前的线程,不会退出主进程
(四)被动退出:
示例代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *routine(void *arg)
{
int i = 0;
while(1)
{
printf("i = %d\n", i++);
sleep(1);
}
}
int main(int argc, char const *argv[])
{
int a = 20;
pthread_t pid;
//创建一个线程
pthread_create(&pid, NULL, routine, (void *)(&a));
pthread_t pid1;
pthread_create(&pid1, NULL, routine, (void *)(&a));
// pthread_cancel(pid);
sleep(4);
pthread_cancel(pid);
sleep(5);
pthread_cancel(pid1);
pthread_join(pid, NULL);
return 0;
}
复制代码
运行结果:
三、线程属性(了解)
(1)分离属性
(2)调度策略
(3)栈大小和警戒区大小
复制代码
线程属性函数使用步骤
1,定义线程属性变量,并且使用 pthread_attr_init( )初始化。
pthread_attr_t attr; pthread_attr_init(attr );
复制代码
2,使用 pthread_attr_setXXX( )来设置相关的属性。
3,使用该线程属性变量创建相应的线程。
4,使用 pthread_attr_destroy( )销毁该线程属性变量。
pthread_attr_destroy(attr );
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
复制代码
分离属性:
(1)分离:让pthread_join无效,线程退出不会进入僵尸态,不会传出数据
(2)接合:与上面相反
【默认是接合】
复制代码
2、调度策略
看线程什么时候先调度,什么时候后调度
设置静态优先级、动态优先级
栈大小和警戒区大小
4、退出处理例程
线程小练习代码
1、利用有名管道实现双向通信,可以进行消息的互发,利用线程。
示例代码
jack:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#define FIFO_PATHNAME1 "/tmp/myfifo1"
#define FIFO_PATHNAME2 "/tmp/myfifo2"
int flag;
void *read_sig(void *arg)
{
char r_buf[1024] = {0};
while(1)
{
bzero(r_buf, sizeof(r_buf));
read(*(int *)arg, r_buf, sizeof(r_buf));
printf("rose:%s\n", r_buf);
if (strcmp(r_buf, "quit") == 0)
{
kill(flag, 9);
break;
}
}
}
int main(int argc, char const *argv[])
{
flag = getpid();
signal(9, SIG_DFL);
//创建有名管道(判断是否有这个文件)
if(access(FIFO_PATHNAME1, F_OK)) //判断这个文件是否存在
{
int ret = mkfifo(FIFO_PATHNAME1, 0644);
if (ret == -1)
{
perror("mkfifo FIFO_PATHNAME1");
return -1;
}
}
if(access(FIFO_PATHNAME2, F_OK)) //判断这个文件是否存在
{
int ret = mkfifo(FIFO_PATHNAME2, 0644);
if (ret == -1)
{
perror("mkfifo FIFO_PATHNAME2");
return -1;
}
}
//打开有名管道
int fd1 = open(FIFO_PATHNAME1, O_RDWR);
if (fd1 == -1)
{
perror("open");
return -1;
}
//打开有名管道
int fd2 = open(FIFO_PATHNAME2, O_RDWR);
if (fd2 == -1)
{
perror("open");
return -1;
}
printf("有名管道打开成功\n");
pthread_t pid;
pthread_create(&pid, NULL, read_sig, (void *)&fd1);
char w_buf[1024] = {0};
while(1)
{
//写数据
bzero(w_buf, sizeof(w_buf));
scanf("%s", w_buf);
write(fd2, w_buf, strlen(w_buf));
if (strcmp(w_buf, "quit") == 0)
{
break;
}
}
return 0;
}
复制代码
rose:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#define FIFO_PATHNAME1 "/tmp/myfifo1"
#define FIFO_PATHNAME2 "/tmp/myfifo2"
int flag = 0;
void *read_sig(void *arg)
{
char r_buf[1024] = {0};
while(1)
{
bzero(r_buf, sizeof(r_buf));
read(*(int *)arg, r_buf, sizeof(r_buf));
printf("jack:%s\n", r_buf);
if (strcmp(r_buf, "quit") == 0)
{
kill(flag, 9);
break;
}
}
}
int main(int argc, char const *argv[])
{
flag = getpid();
signal(9, SIG_DFL);
//创建有名管道(判断是否有这个文件)
if(access(FIFO_PATHNAME1, F_OK)) //判断这个文件是否存在
{
int ret = mkfifo(FIFO_PATHNAME1, 0644);
if (ret == -1)
{
perror("mkfifo FIFO_PATHNAME1");
return -1;
}
}
if(access(FIFO_PATHNAME2, F_OK)) //判断这个文件是否存在
{
int ret = mkfifo(FIFO_PATHNAME2, 0644);
if (ret == -1)
{
perror("mkfifo FIFO_PATHNAME2");
return -1;
}
}
//打开有名管道
int fd1 = open(FIFO_PATHNAME1, O_RDWR);
if (fd1 == -1)
{
perror("open");
return -1;
}
//打开有名管道
int fd2 = open(FIFO_PATHNAME2, O_RDWR);
if (fd2 == -1)
{
perror("open");
return -1;
}
printf("有名管道打开成功\n");
pthread_t pid;
pthread_create(&pid, NULL, read_sig, (void *)&fd2);
char w_buf[1024] = {0};
while(1)
{
//写数据
if (flag == 1)
{
break;
}
bzero(w_buf, sizeof(w_buf));
scanf("%s", w_buf);
write(fd1, w_buf, strlen(w_buf));
if (strcmp(w_buf, "quit") == 0)
{
break;
}
}
return 0;
}
复制代码
运行结果:
2、利用消息队列实现双向通信,可以进行消息的互发,利用线程。
示例代码:
jack
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#define F1 1L
#define F2 2L
struct msgbuf
{
long mtype; // 消息的标识
char mtext[50]; // 消息的正文
};
void *msg_recv(void *arg)
{
struct msgbuf msg = {.mtype = F2};
while(1)
{
msgrcv(*(int *)arg, &msg, sizeof(msg.mtext), F2, 0);
printf("rose:%s\n", msg.mtext);
}
}
int main(int argc, char const *argv[])
{
//获取键值
key_t key = ftok(".", 12);
if (key == -1)
{
perror("ftok");
return -1;
}
//获取消息队列的ID
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1)
{
perror("msgget");
return -1;
}
pthread_t pid;
pthread_create(&pid, NULL, msg_recv, (void *)&msgid);
//发送数据
//定义发送数据结构体变量
struct msgbuf msg = {.mtype = F1};
while(1)
{
scanf("%s", msg.mtext);
msgsnd(msgid, &msg, strlen(msg.mtext)+1, 0);
if (strcmp(msg.mtext, "quit") == 0)
{
break;
}
}
return 0;
}
复制代码
rose
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#define F1 1L
#define F2 2L
struct msgbuf
{
long mtype; // 消息的标识
char mtext[50]; // 消息的正文
};
void *msg_recv(void *arg)
{
struct msgbuf msg = {.mtype = F1};
while(1)
{
msgrcv(*(int *)arg, &msg, sizeof(msg.mtext), F1, 0);
printf("jack:%s\n", msg.mtext);
}
}
int main(int argc, char const *argv[])
{
//获取键值
key_t key = ftok(".", 12);
if (key == -1)
{
perror("ftok");
return -1;
}
//获取消息队列的ID
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1)
{
perror("msgget");
return -1;
}
pthread_t pid;
pthread_create(&pid, NULL, msg_recv, (void *)&msgid);
//发送数据
//定义发送数据结构体变量
struct msgbuf msg = {.mtype = F2};
while(1)
{
scanf("%s", msg.mtext);
msgsnd(msgid, &msg, strlen(msg.mtext)+1, 0);
if (strcmp(msg.mtext, "quit") == 0)
{
break;
}
}
return 0;
}
复制代码
运行结果
3、主线程打开一个文件,创建两个线程,A线程每过一秒往文件里面写入一条A:线程1,B线程每过一秒,往文件里面写入一条B:线程2。主线程过一分钟之后关闭两个线程
要求:把文件描述符通过传参传给线程。
示例代码
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
void *func1(void *arg)
{
char *p = "A:线程1\r\n";
while(1)
{
sleep(1);
write(*(int *)arg, p, strlen(p));
}
}
void *func2(void *arg)
{
char *p = "B:线程2\r\n";
while(1)
{
sleep(1);
write(*(int *)arg, p, strlen(p));
}
}
void func3(int m)
{
printf("3m = %d\n", m);
}
void func4(int m)
{
printf("4m = %d\n", m);
}
int main(int argc, char const *argv[])
{
int fd = open("test3.txt", O_RDWR);
void *p[2] = {func1, func2};
// void (*p1[2])(int) = {func3, func4};
pthread_t pid[2];
for (int i = 0; i < 2; ++i)
{
pthread_create(&pid[i], NULL, p[i], (void *)&fd);
}
int i = 0;
while(1)
{
i++;
sleep(1);
printf("i = %d\n", i);
if (i == 10)
{
break;
}
}
return 0;
}
复制代码
运行结果
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END