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

Linux从入门到开发实战(C/C++)Day10-线程

1.概念:
        线程是一个函数,是os调度的基本单位
        Linux内核在2.2版本之前,是没有线程的概念,只有有限个进程(4096)
                在2.4版本中,有了线程的概念,而且可以有无数个
        主线程一般默认main函数
        分支线程一般是被创建的线程
        一个进程中至少有一个线程,同一进程中的线程共享进程的所有资源(全局变量)
        多个线程可以用一个线程函数,函数中的局部变量是每个线程独有的(不共用)

    2.如何创建并启用线程
        #include <pthread.h>

        pthread_create    创建线程(man pthread_create)
        int pthread_create(
                            pthread_t *thread,                         //线程id,作为返回参数
                            const pthread_attr_t *attr,              //线程属性
                              void *(*start_routine) (void *),      //线程函数
                            void *arg                                        //线程函数参数
                            );
         Compile and link with -pthread.    
         //程序中使用了这个函数,生成时要链接库 gcc *.c -l pthread 

         线程传参
             参数为void* 注意类型转换即可

        线程结束:
            自然结束
                线程函数结束
            主线程结束
                主线程结束时会结束掉分支线程
            用pthread_exit
                与exit函数类似
             其他线程发送信号给线程
                 pthread_kill

             其他线程结束线程
                 pthread_cancel

             获取线程返回值
                 pthread_join 阻塞函数,直到获取的线程结束,这个函数才返回,一般是用它等待某线程结束
    
    3.临界数据
        多个线程同时运行,都可以访问到的数据叫做临界数据
        多个线程同时访问临界数据后,导致临界数据脏(结果不对)

    4.线程同步
        通过各种方式让并发执行的线程不同时访问同一块区域
            原子锁    atomic_add(老版本的有,现在不支持了)特性:不可分割

            自旋锁    pthread_spinlock_init
                一直在循环等待
                互斥锁建立锁消耗多,当线程阻塞的时候不会一直循环,处于阻塞情况下不消耗资源
                自旋锁建立锁消耗资源少,当线程阻塞的时候一直循环检查锁是否可用,处于阻塞情况下消耗资源
                互斥锁适用于阻塞时间长的情况
                自旋锁适用于阻塞时间短的情况
                pthread_spinlock_t
                pthread_spinlock_init
                pthread_spinlock_lock
                pthread_spinlock_unlock
                pthread_spinlock_destory
                也可以和条件变量联用让线程相对均匀分配
            信号量(旗语)

            读写锁    pthread_rwlock_init
                有两把锁,一把 读 锁,一把 写 锁,读写、写写相斥,读读相容
                pthread_rwlock_t        创建
                pthread_rwlock_init        初始化
                pthread_rwlock_wrlock    加写锁
                pthread_rwlock_rdlock    加读锁
                pthread_rwlock_unlock    解锁
                pthread_rwlock_destory    销毁


            互斥量    pthread_mutex_init
                相互排斥,某个线程加了锁,其他线程在解锁前都阻塞
                可能某个线程很久都抢不到加锁权(不能均匀分配cpu时间片)
                如果希望多个线程相对公平,就要和条件变量一起使用

            条件变量    pthread_cond_init
                pthread_cond_t             创建
                pthread_cond_init         初始化
                pthread_cond_wait         等待信号(阻塞式)
                pthread_cond_signal     向某个线程发送信号
                pthread_cond_bordcast     向所有线程发送信号
                pthread_cond_destroy     销毁信号

        通用编程模型:
            1.创建锁/变量
            2.初始化
            3.使用
                加锁
                解锁
            4.销毁锁
        死锁:线程卡死
            死锁产生必须要满足的四点:
                1)请求且保持
                2)不可撤销
                3)不可摧毁
                4)循环请求
        粒度:一把锁锁住的范围
            粒度小 锁越多 越灵活 消耗的资源越多
            粒度大 锁越少 越不灵活 消耗的资源越少

// pthread
void *func(void *arg)
{
	int n = 0;
	while (1)
	{
		printf("分支线程:%d\n", n--);
		sleep(2);
	}
}
void _pthread()
{
	int n = 0;
	pthread_t pt = 0;
	printf("pt: %u\n", pt);
	pthread_create(&pt, NULL, func, NULL);
	while (1)
	{
		printf("主线程:%d\n", n++);
		sleep(2);
	}
}

// 1.创建互斥量
pthread_mutex_t mutex;
int num = 0;
void f_mutex()
{
	for (size_t i = 0; i < 1000; i++)
	{
		// 3.使用互斥量前加锁
		pthread_mutex_lock(&mutex);
		num++;
		// 用完解锁
		pthread_mutex_unlock(&mutex);
	}
}
void _pthread_mutex()
{
	pthread_t t1, t2;

	/// 2.初始化互斥量
	pthread_mutex_init(&mutex, NULL);

	pthread_create(&t1, NULL, f_mutex, NULL);
	pthread_create(&t2, NULL, f_mutex, NULL);

	pthread_join(t1, NULL);
	pthread_join(t2, NULL);

	printf("%d\n", num);

	// 4.销毁互斥量
	pthread_mutex_destroy(&mutex);
}
//
// 互斥量与条件变量一起用
pthread_mutex_t mutex_1;
pthread_cond_t cond_1;
void f1()
{
	while (1)
	{
		pthread_mutex_lock(&mutex_1);
		pthread_cond_wait(&cond_1, &mutex_1);
		printf("===线程1===\n");
		sleep(1);
		pthread_mutex_unlock(&mutex_1);
	}
}
void f2()
{
	while (1)
	{
		pthread_mutex_lock(&mutex_1);
		pthread_cond_wait(&cond_1, &mutex_1);
		printf("---线程2---\n");
		sleep(1);
		pthread_mutex_unlock(&mutex_1);
	}
}
void f3()
{
	while (1)
	{
		pthread_mutex_lock(&mutex_1);
		pthread_cond_wait(&cond_1, &mutex_1);
		printf("+++线程3+++\n");
		sleep(1);
		pthread_mutex_unlock(&mutex_1);
	}
}
void _pthread_mutexAndCond()
{
	pthread_t t1, t2, t3;

	/// 2.初始化
	pthread_mutex_init(&mutex_1, NULL);
	pthread_cond_init(&cond_1, NULL);

	pthread_create(&t1, NULL, f1, NULL);
	pthread_create(&t2, NULL, f2, NULL);
	pthread_create(&t3, NULL, f3, NULL);

	while (1)
	{
		pthread_cond_signal(&cond_1);
		sleep(1);
	}

	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	pthread_join(t3, NULL);

	// 4.销毁
	pthread_mutex_destroy(&mutex_1);
	pthread_cond_destroy(&cond_1);
}


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

相关文章:

  • 2023年MathorCup数学建模B题城市轨道交通列车时刻表优化问题解题全过程文档加程序
  • 【机器学习】如何配置anaconda环境(无脑版)
  • 解锁微前端的优秀库
  • 代码随想录第二十一天| 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树
  • vue2.7.14 + vant + vue cli脚手架转vite启动运行问题记录
  • 在 Service Worker 中caches.put() 和 caches.add()/caches.addAll() 方法他们之间的区别
  • Git使用经验总结6-删除远端历史记录
  • ​zookeeper集群配置与启动
  • CentOs 入门必备基础知识详细讲解
  • 谈一谈幽默的力量
  • Java 正则表达式详解
  • pandas中基于范围条件进行表连接
  • JAVA开源项目 校园管理系统 计算机毕业设计
  • C++基础知识6 vector
  • 装饰器模式decorator
  • 3. 轴指令(omron 机器自动化控制器)——>MC_HomeWithParameterMC_Move
  • 怎么给DataX的Json配置文件传参
  • java后端请求调用三方接口
  • 【Hot100】LeetCode—763. 划分字母区间
  • C++战列舰小游戏Lv. 1.4版本(半成品)
  • 【STM32实物】基于STM32设计的18650锂电池电量(电压/电流)检测系统——采用电阻分压法、均值滤波及ADC测量—文末工程资料下载
  • 在Linux中从视频流截取图片帧(ffmpeg )
  • 西门子1200/1500PLC什么时候需要设置网关地址
  • TCP全连接队列和tcpdump抓包
  • MinIO【部署 02】Linux集群版本及Windows单机版、单机多目录版、分布式版(cmd启动脚本及winsw脚本分享)
  • 模版方法模式template method