线程初入门

线程的引入:

由于进程的地址空间是私有的,在进行切换的时候,系统开销较大,为了解决这个问题,提高系统的性能,引入轻量级别进程的概念,将这种轻量级别的进程称为线程。

进程和线程的区别:

进程是系统资源分配的基本单位

线程是系统任务调度的基本单位

线程的资源

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)线程创建

image.png

image.png

线程例程函数需要自己定义,函数接口如下:

void *routine(void *arg)
{
}

(二)结合指定线程

image.png

1,如果线程退出时没有退出值,那么retval可以指定为 NULL。
2,pthread_join()指定的线程如果尚在运行,那么他将会阻塞等待。
3,pthread_tryjoin_np()指定的线程如果尚在运行,那么他将会立即出错返回。

(三)线程退出

image.png

注意:局部变量在函数退出的时候,会释放,如果线程函数需要返回数据,就必须要保证这个数据退出函数依然有效,所以我们建议,要么用静态数据,要么用堆空间
pthread_exit()退出线程,只会退出当前的线程,不会退出主进程

(四)被动退出:

image.png

示例代码

#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;
}

运行结果:

image.png

三、线程属性(了解)

(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);

分离属性:

image.png

(1)分离:让pthread_join无效,线程退出不会进入僵尸态,不会传出数据
(2)接合:与上面相反
【默认是接合】

2、调度策略

看线程什么时候先调度,什么时候后调度

image.png

image.png

image.png

设置静态优先级、动态优先级

image.png

image.png

栈大小和警戒区大小

image.png

4、退出处理例程

image.png

线程小练习代码

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;
}

运行结果:

image.png

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;
}

运行结果

image.png

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;
}

运行结果

image.png

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jianglongyin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值