当前位置: 首页 > article >正文

Linux 多线程编程

韦东山的例程

所谓线程,就是操作系统所能调度的最小单位。普通的进程,只有一个线程在执行对应的逻辑。我们可以通过多线程编程,使一个进程可以去执行多个不同的任务。相比多进程编程而言,线程享有共享资源,即在进程中出现的全局变量,每个线程都可以去访问它,与进程共享内存空间,使得系统资源消耗减少。

创建线程,使用 pthread_create 函数

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

该函数第一个参数为 pthread_t 指针,用来保存新建线程的线程号

  • 第二个参数表示了线程的属性,一般传入 NULL 表示默认属性
  • 第三个参数是一个函数指针,就是线程执行的函数。这个函数返回值为 void*,形参为void*
  • 第四个参数则表示为向线程处理函数传入的参数,若不传入,可用 NULL 填充


第一个例程:
创线程

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>


static void *my_thread_func (void *data)
{
	while (1)
	{
		sleep(1);
	}
}


int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	
	/* 1. 创建"接收线程" */
	//my_thread_func将在线程创建时运行
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}


	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		sleep(1);
	}
	return 0;
}

编译报错则 加 -lpthread 链接别的库

运行后查看进程 ps 查看线程 ps -T

第二个例程:
目的是创建一个接收线程,这个接收线程能接收主线程给的数据,主线程从标准输入得到数据

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

static char g_buf[1000];
//线程可以访问全局变量,加个标记位
static int g_hasData = 0;
static void *my_thread_func (void *data)
{
	while (1)
	{
		//sleep(1);
		/* 等待通知 */
		while (g_hasData == 0);

		/* 打印 */
		printf("recv: %s\n", g_buf);
		g_hasData = 0;
	}

	return NULL;
}


int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	
	/* 1. 创建"接收线程" */
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}


	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		fgets(g_buf, 1000, stdin);

		/* 通知接收线程 */
		g_hasData = 1;
	}
	return 0;
}

但是,这样的代码使得cpu资源消耗很大,fgets用于接收,未发送数据时,它就会处于休眠状态,然而 while(g_hasData == 0) 一直在运行,我们应该考虑让他修眠

///
第三个例程:
应该让主函数收到信号量再去唤醒接收线程

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>

static char g_buf[1000];
static sem_t g_sem;
static void *my_thread_func (void *data)
{
	while (1)
	{
		//sleep(1);
		/* 等待通知 */
		//while (g_hasData == 0);
		sem_wait(&g_sem);

		/* 打印 */
		printf("recv: %s\n", g_buf);
	}

	return NULL;
}


int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;

	sem_init(&g_sem, 0, 0);
	
	/* 1. 创建"接收线程" */
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}


	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		fgets(g_buf, 1000, stdin);

		/* 通知接收线程 */
		sem_post(&g_sem);
	}
	return 0;
}

这样cpu占用高解决了
但是,接收线程在打印g_buf的时候,主线程也是在运行的,那么,接收线程在打印g_buf的时候如果输入了字符串,可能接收线程打印出来的可能半截老数据,半截新数据

/

第四个例程:
我们引入互斥量mutex
第一次代码,主线程接受没问题,子线程无法执行,无法打印
原因是主线程解锁后,可能立马又进入while,这个时候sem_post(&g_sem)却还没有到达子线程!
互斥量总是被主线程获得,子线程没那么快反应,无法获取互斥量

while (1)
	{
		pthread_mutex_lock(&g_tMutex);
		fgets(buf, 1000, stdin);
		pthread_mutex_unlock(&g_tMutex);
		/* 通知接收线程 */
		sem_post(&g_sem);
	}

主线程获得互斥量时间太长,我们想办法让他短一点
读数据读临时buf,得到数据后拷入g_buf中去!
这样,主线程占用互斥锁的时间主要就是数据拷贝和设置g_hasData变量的时间,相比之前直接在获取互斥锁的情况下读取到g_buf,减少了在互斥锁保护下的fgets操作时间(fgets可能会因为等待用户输入而长时间占用互斥锁),所以先输完再说,加大子线程运行几率

while (1)
	{
		fgets(buf, 1000, stdin);
		pthread_mutex_lock(&g_tMutex);
		memcpy(g_buf, buf, 1000);
		pthread_mutex_unlock(&g_tMutex);

		/* 通知接收线程 */
		sem_post(&g_sem);
	}

这是完整代码

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>

static char g_buf[1000];
static sem_t g_sem;
//定义互斥量
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;

static void *my_thread_func (void *data)
{
	while (1)
	{
		//sleep(1);
		/* 等待通知 */
		//while (g_hasData == 0);
		sem_wait(&g_sem);

		/* 打印 */
		pthread_mutex_lock(&g_tMutex);
		printf("recv: %s\n", g_buf);
		pthread_mutex_unlock(&g_tMutex);
	}

	return NULL;
}


int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	char buf[1000];

	sem_init(&g_sem, 0, 0);
	
	/* 1. 创建"接收线程" */
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}


	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		fgets(buf, 1000, stdin);
		pthread_mutex_lock(&g_tMutex);
		memcpy(g_buf, buf, 1000);
		pthread_mutex_unlock(&g_tMutex);

		/* 通知接收线程 */
		sem_post(&g_sem);
	}
	return 0;
}


第五个例程:

同步操作之条件变量

和互斥量同时使用
在这里插入图片描述

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>

static char g_buf[1000];
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;

static void *my_thread_func (void *data)
{
	while (1)
	{
		//sleep(1);
		/* 等待通知 */
		//while (g_hasData == 0);
		pthread_mutex_lock(&g_tMutex);
		pthread_cond_wait(&g_tConVar, &g_tMutex);	

		/* 打印 */
		printf("recv: %s\n", g_buf);
		pthread_mutex_unlock(&g_tMutex);
	}

	return NULL;
}


int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	char buf[1000];
	
	/* 1. 创建"接收线程" */
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}


	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		fgets(buf, 1000, stdin);
		pthread_mutex_lock(&g_tMutex);
		memcpy(g_buf, buf, 1000);
		pthread_cond_signal(&g_tConVar); /* 通知接收线程 */
		pthread_mutex_unlock(&g_tMutex);
	}
	return 0;
}



http://www.kler.cn/a/374962.html

相关文章:

  • uni-app实现app展示进度条在线更新以及定时更新提醒
  • Nginx 文件名逻辑漏洞(CVE-2013-4547)
  • ts:使用fs内置模块简单读写文件
  • 2024年1024程序人生总结
  • 【MySQL】 运维篇—安全管理:防止SQL注入与其他安全威胁
  • Git中HEAD 处于游离状态(HEAD detached)的原因
  • 视频文件损坏无法播放怎么办?有什么办法可以修复视频吗?
  • PPT编辑限制密码忘记了怎么办?2个方法快速取消编辑限制
  • CSS3新增盒子属性(三)
  • 无需手动部署的正式版comfyUI是否就此收费?开源等同免费?
  • 【AI抠图整合包及教程】Meta SAM 2:视觉分割的革命性飞跃
  • 2024wdb|misc01
  • C++基础:C++错误
  • liunx CentOs7安装MQTT服务器(mosquitto)
  • 单片机串口和电脑串口连接
  • 使用Vue3DraggableResizable组件实现拖拽拉伸
  • Node.js与Python的交互:使用node-pyrunner模块
  • Python轴承故障诊断 (15)基于CNN-Transformer的一维故障信号识别模型
  • vue2 的12种 vs vue3 的9种组件通信整理
  • 【Wi-Fi】Wi-Fi 7(802.11be) Vs Wi-Fi 8 (802.11bn)
  • 「Mac畅玩鸿蒙与硬件19」鸿蒙UI组件篇9 - 自定义动画实现
  • 臻于智境 安全护航 亚信安全受邀出席新华三智算新品发布会
  • vue3二次封装UI组件
  • 深入理解 Dockerfile 和 docker-compose[实战篇]
  • 持续监控和反馈:工具与方法详解
  • Python数据类型:数字