【Linux】线程互斥

2023-05-16

目录

1.进程线程间的互斥相关背景概念

2.互斥量mutex

2.1.基本概念

2.2.售票系统举例

2.3.解释

3.互斥量的接口

3.1.初始化互斥量

3.1.1.静态分配

3.1.2.动态分配(pthread_mutex_init)

3.2.销毁互斥量

3.3.互斥量加锁和解锁

3.3.1.阻塞加锁(pthread_mutex_lock)

3.3.2.非阻塞加锁(pthread_mutex_trylock)

3.3.2.解锁(pthread_mutex_unlock)

3.4.改进上面的售票系统

4.互斥量实现原理探究

RAII风格的加锁

5.可重入VS线程安全

5.1.概念

5.2.常见的线程不安全的情况

5.3.常见的线程安全的情况

5.4.常见不可重入的情况

5.5.常见可重入的情况

5.6.可重入与线程安全联系

5.7.可重入与线程安全区别

6.死锁

6.1.概念:

6.2.死锁四个必要条件

6.3.避免死锁

6.4.避免死锁的算法(了解)




1.进程线程间的互斥相关背景概念

  • 临界资源:多线程执行流共享的资源就叫做临界资源
  • 临界区:每个线程内部,访问临界资源的代码,就叫做临界区
  • 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
  • 原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成



2.互斥量mutex

2.1.基本概念

  • 大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。
  • 只有很少一部分代码或者数据线程间共享
  • 但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量(共享资源),可以通过数据的共享,完成线程之间的交互。
  • 多个线程并发的操作共享变量,会带来一些问题。比如:数据不一致问题
  • 解决方法就是对临界区加锁。让每个线程串行的访问临界资源---这就是互斥访问。
  • 这个时候多个线程对临界资源的访问就是原子的。-----原子性。
  • 对资源进行操作的时候,如果只用了一条汇编语言就能完成,就是原子的。这是原子性的一种情况。常见的++,-- 不是原子的。

2.2.售票系统举例

// 定义一个全局变量。
int g_val = 10000;
//总票数是10000

//抢票函数
void *fun(void *argc)
{
    std::string str = (char *)argc;
    while (true){
        //判断当前是否有票
        if (g_val > 0){
            usleep(100);
            //这一步等待是精华所在。慢慢体会。
            std::cout << str << " runing...."<<"当前正在进行抢票!! "
                      << " g_val:" << g_val << std::endl;
                      g_val--;
        }
        else{
            break;
        }
    }
    return nullptr;
}

int main()
{
    //假如有五个人在同时抢票。
    Thread tp1(fun, 1, (void *)"thread-1");
    Thread tp2(fun, 2, (void *)"thread-2");
    Thread tp3(fun, 3, (void *)"thread-3");
    Thread tp4(fun, 4, (void *)"thread-4");
    Thread tp5(fun, 5, (void *)"thread-5");

    tp1.join();
    tp2.join();
    tp3.join();
    tp4.join();
    tp5.join();

    //这是一个测试代码,目的是为了让我们测出抢票,最后把票抢到负数,出现票多卖的问题。
    //就需要尽可以的让多个线程交叉执行。
    //本质是让调度器频繁调度,线程频繁切换。 //线程发生线程切换情况:
    //1.时间片到了。
    //2.来了更高优先级的线程。
    //3.线程等待的时候。
    //线程在内核态返回用户态的时候,OS会对调度状态进行检测,如果满足上述条件,直接发送线程切换。
    

    return 0;
}

运行结果:

 很诧异抢票竟然抢到了 负数。不可思议。

2.3.解释

原因就是每一个线程对应的抢票过程不是原子的,一个进程抢到一半,突然被调度器切走了。但是票数还没来得及修改,另一个进程认为还有票,也取到了票,但是实际情况是,票已经为负数了。总结就是:

  • if 语句判断条件为真以后,代码可以并发的切换到其他线程。
  • usleep 这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段。
  • --num 操作本身就不是一个原子操作。

分为三步汇编完成:

  •  load :将共享变量ticket从内存加载到寄存器中
  • update : 更新寄存器里面的值,执行-1操作
  • store :将新值,从寄存器写回共享变量ticket的内存地址

要解决以上问题,需要做到三点:

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
  • 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
  • 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。

要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量(mutex)!!!!!!

 



3.互斥量的接口

3.1.初始化互斥量

初始化互斥量有两种方法:

  1. 方法1,静态分配:
  2. 方法2,动态分配:

如果一个锁是局部的就必须调用pthread_mutex_init和pthread_mutex_destroy进程初始化(动态分配)。如果一个锁是全局的可以直接静态分配(PTHREAD_MUTEX_INITIALIZER)。

3.1.1.静态分配

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

3.1.2.动态分配(pthread_mutex_init)

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);
参数:
    mutex:要初始化的互斥量
    attr:nullptr

3.2.销毁互斥量

销毁互斥量需要注意:

  • 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化(静态分配)的互斥量不需要销毁
  • 不要销毁一个已经加锁的互斥量
  • 已经销毁的互斥量,要确保后面不会有线程再尝试加锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

3.3.互斥量加锁和解锁

3.3.1.阻塞加锁(pthread_mutex_lock)

注意:

  • 互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功
  • 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,那么pthread_ lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁

3.3.2.非阻塞加锁(pthread_mutex_trylock)

3.3.2.解锁(pthread_mutex_unlock)

3.4.改进上面的售票系统

// 定义一个全局变量。
int g_val = 10000;
// 总票数是10000

//pthread_mutex_t mutex ;
pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

// 抢票函数
void *fun(void *argc)
{
    std::string str = (char *)argc;
    while (true)
    {
        // 判断当前是否有票
        pthread_mutex_lock(&mutex);
        if (g_val > 0)
        {
            usleep(100);
            std::cout << str << " runing...."
                      << "当前正在进行抢票!! "
                      << " g_val:" << g_val << std::endl;
            g_val--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        //pthread_mutex_unlock(&mutex);//写这里不对。
        usleep(100);
        // 加锁之后每个线程是串行的访问此临界区的。所以程序会变慢一点点。
        // 注意这里需要等待一下,不会都是一个线程会一直抢票,其他线程会出现饥饿状态。
        // 线程互斥只是规定了线程互斥访问,没有规定谁优先执行。就会出现竞争,就会出现饥饿线程。
    }
    return nullptr;
}

int main()
{
    // 假如有五个人在同时抢票。
    Thread tp1(fun, 1, (void *)"thread-1");
    Thread tp2(fun, 2, (void *)"thread-2");
    Thread tp3(fun, 3, (void *)"thread-3");
    Thread tp4(fun, 4, (void *)"thread-4");
    Thread tp5(fun, 5, (void *)"thread-5");

    tp1.join();
    tp2.join();
    tp3.join();
    tp4.join();
    tp5.join();

    return 0;
}

 



4.互斥量实现原理探究

经过上面的例子,现在我相信大家已经可以很好的理解互斥锁是什么了,互斥锁是用来保护共享资源的,共享资源是能被所有线程访问到的资源,能被多个线程访问,那么就会出现数据不一致的问题导致数据不安全。同样互斥锁也是可以被多个线程访问,互斥锁的数据安全是由于,lock和unlock的两个操作是原子的。

前面我们讲到,如果加锁成功,会成功返回,如果加锁失败,此线程会阻塞自己进入休眠状态。

谁持有锁(加锁成功)谁进入临界区访问临界资源。

经过上面的讲解,大家已经意识到单纯的 i++ 或者 ++i 都不是原子的,有可能会有数据一致性问题
为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。

现在我们把lock和unlock的伪代码改一下:

RAII风格的加锁

//Mutes.hpp

//Mutex.hpp

#pragma once

#include <iostream>
#include <pthread.h>
#include <cassert>

class Mutex
{
public:
    Mutex(pthread_mutex_t *pmutex = nullptr) : _pmutex(pmutex)
    {
    }

    void *lock()
    {
        if (_pmutex)
        {
            pthread_mutex_lock(_pmutex);
        }
        else
        {
            std::cout << "互斥量未初始化!!" << std::endl;
        }
    }
    void *unlock()
    {
        if (_pmutex)
        {
            pthread_mutex_unlock(_pmutex);
        }
        else
        {
            std::cout << "互斥量未初始化!!" << std::endl;
        }
    }

private:
    pthread_mutex_t *_pmutex;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t* mutex = nullptr):_mutex(mutex)
    {
        _mutex.lock();//在构造函数中加锁
    }
    ~LockGuard()
    {
        _mutex.unlock();//在析构函数中解锁
    }
private:
    Mutex _mutex;
};

//test,cc

#include "Mutex.hpp"
// 定义一个全局变量。
int g_val = 10000;
// 总票数是10000
pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
// 只有全局互斥量和静态互斥量才能这样初始化

// 抢票函数
void *fun(void *argc)
{
    std::string str = (char *)argc;
    while (true)
    {
        // 判断当前是否有票
        {
            LockGuard lockguard(&mutex);//RAII风格的加锁
            //可以是用花括号{ }来控制临界区范围。
            //构建代码块即可
            if (g_val > 0)
            {
                usleep(100);
                std::cout << str << " runing...."
                          << "当前正在进行抢票!! "
                          << " g_val:" << g_val << std::endl;
                g_val--;
            }
            else
            {
                break;
            }
        }
        usleep(100);
    }
    return nullptr;
}

int main()
{
    // 假如有五个人在同时抢票。
    Thread tp1(fun, 1, (void *)"thread-1");
    Thread tp2(fun, 2, (void *)"thread-2");
    Thread tp3(fun, 3, (void *)"thread-3");
    Thread tp4(fun, 4, (void *)"thread-4");
    Thread tp5(fun, 5, (void *)"thread-5");

    tp1.join();
    tp2.join();
    tp3.join();
    tp4.join();
    tp5.join();

    return 0;
}



5.可重入VS线程安全

5.1.概念

  • 线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。
  • 重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。

5.2.常见的线程不安全的情况

  • 不保护共享变量的函数
  • 函数状态随着被调用,状态发生变化的函数
  • 返回指向静态变量指针的函数
  • 调用线程不安全函数的函数

5.3.常见的线程安全的情况

  • 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的。
  • 类或者接口对于线程来说都是原子操作。
  • 多个线程之间的切换不会导致该接口的执行结果存在二义性。


5.4.常见不可重入的情况

  • 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
  • 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构
  • 可重入函数体内使用了静态的数据结构

5.5.常见可重入的情况

  • 不使用全局变量或静态变量
  • 不使用用malloc或者new开辟出的空间
  • 不调用不可重入函数
  • 不返回静态或全局数据,所有数据都有函数的调用者提供
  • 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

5.6.可重入与线程安全联系

  • 函数是可重入的,那就是线程安全的。
  • 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题。
  • 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

5.7.可重入与线程安全区别

  • 可重入函数是线程安全函数的一种。
  • 线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
  • 如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。



6.死锁

6.1.概念:

死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
 

6.2.死锁四个必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系

6.3.避免死锁

  • 破坏死锁的四个必要条件
  • 加锁顺序一致
  • 避免锁未释放的场景
  • 资源一次性分配

6.4.避免死锁的算法(了解)

  • 死锁检测算法(了解)
  • 银行家算法(了解)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux】线程互斥 的相关文章

  • SpringBoot3+最新MybatisPlus+Mysql与TDengine双数据源

    前言 昨天写的idea 43 Apifox uploader插件 43 apifox新年第一天上班就上榜了 xff0c 真是不错 今天来补一篇 xff0c 本来应该是在前一篇之前发的 实际上就是最新的springBoot集成最新的mybat
  • 业务平台扩展支持TDengine时序数据库方案

    1 场景与架构 1 1业务架构 这里涉及项目隐私 xff0c 架构图不方便公开 大致情况就是 xff1a 应用层的园区畅行 生态宜居 安全守护是我方要交付的系统 平台层的物联网感知中台是我方平台 1 2数据架构 从数据架构看 xff0c 园
  • 物联网平台+业务平台基本架构设计与优化想法

    前言 目前的交付底座有点老 xff0c 而且集成的有点杂 xff0c 计划是要升级下 xff0c 先说想法 xff0c 看领导做不做 1 业务平台定位 我们的愿景 xff1a 通过物联平台赋能 xff0c 让数据产生价值 为客户提供可视化的
  • Qt 正则表达式匹配失败的一个原因

    在Qt中做正则表达式时 xff0c 遇到一个很坑爹的问题 xff0c 还是经验不足导致 在正则表达式中 xff0c 有很多需要元字符 xff0c 是需要使用普通字符加转义符号搭配使用的 比如 w xff0c s 对于这类字符 xff0c 在
  • C++对象模型(整理)

    C 43 43 对象模型 1 何为C 43 43 对象模型 xff1f C 43 43 对象模型可以概括为以下2部分 xff1a 语言中直接支持面向对象程序设计的部分 面向对象程序设计部分 xff1a 如构造函数 析构函数 虚函数 继承 x
  • MybatisPlus多表查询之零sql编写实现

    1 前言 年初节奏还没有快起来 xff0c 适合做做技术前瞻 xff0c 无论是对个人还是团队都是好事 真要说分享 xff0c 其实感觉也没啥好分享的 xff0c 就像接手新项目一样 xff0c 代码都是自己看 xff0c 别人讲的再多 x
  • 物联网设备流水入库TDengine改造方案

    1 存储机制改造 针对提出的流水查询需求 xff0c 结合设备上报数据讨论后 xff0c 初步定以下几种方案 1 1 ES gt TDengine 1 ES入库有延时 2 ES没有提供数据监听API xff08 不能主动监听 xff0c 实
  • SpringBoot3集成Kafka优雅实现信息消费发送

    前言 首先 xff0c 你的JDK是否已经是8 43 了呢 xff1f 其次 xff0c 你是否已经用上SpringBoot3了呢 xff1f 最后 xff0c 这次分享的是SpringBoot3下的kafka发信息与消费信息 一 场景说明
  • SpringBoot3集成TDengine自适应裂变存储

    前言 首先很遗憾的告诉大家 xff0c 今天这篇分享要关注才可以看了 原因是穷啊 xff0c 现在基本都是要人民币玩家了 xff0c 就比如chatGPT copilot xff0c 这些AI虽然都是可以很好的辅助编码 xff0c 但是都是
  • Github Copilot编码神剑

    前言 今天跟大家分享的其实是现在比较火的Github copilot xff0c 另外 xff0c 就是分享下它的优雅使用 其实知道用这个以后 xff0c 瑟瑟发抖 xff0c 感觉就要失业了 不过真正用过后 xff0c 其实发现这要完全取
  • 一个基于缓存的业务链路日志记录设计方案

    前言 一个简单的全接口日志设计文档 xff0c 先分享设计文档 xff0c 后面分享核心设计代码 1 对外接口设计原则 1 1 基本原则 对外接口要遵守的基本原则 xff1a 1 开闭原则 2 依赖倒置原则 3 单一职责原则 4 接口隔离原
  • 基于缓存的统一请求日志实现

    前言 昨天说的会分享方案的具体实现 xff0c 今天就来分享想核心代码吧 一 思路 我们用的是springboot组服务 xff0c 每个链路通过http请求 所以要实现全链路的日志记录 xff0c 也都是围绕sprinboot做的处理 这
  • SpringBoot集成ChatGPT实现AI聊天

    前言 ChatGPT已经组件放开了 xff0c 现在都可以基于它写插件了 但是说实话我还真没想到可以用它干嘛 xff0c 也许可以用它结合文字语音开发一个老人小孩需要的智能的说话陪伴啥的 今天我就先分享下SpringBoot结合ChatGP
  • activemq 延时队列以及不生效问题

    最近在做的项目中有一个业务涉及到了订单的有效期的问题 xff08 即订单达到一定的时间未支付完成就让该订单失效 xff09 xff0c 于是就想到了延时队列的方式 xff0c 由于项目采用的是activemq xff0c 所以就写了个act
  • elasticsearch的分布式架构原理

    对于全文检索 xff0c lucene是目前最流行的搜索库 以前我们都需要学习使用lucene 基于lucene做相关的开发 xff0c 学习倒排索引的原理 xff0c 而现在 xff0c 我们可以直接使用现成的搜索框架了 xff0c 因为
  • vscode设置代理

    1 通过命令行方式设置 参考 xff1a https liliyuanshangcaoyisuiyikurongyehuoshaobujinchunfengchuiyousheng com 2021 vscode socks5 proxy
  • springboot oa 办公系统,springboot权限系统

    springboot oa 自动化办公系统 43 springboot 完整权限管理系统合二为一 xff01 办公自动化 xff08 OA xff09 是面向组织的日常运作和管理 xff0c 员工及管理者使用频率最高的应用系统 xff0c
  • linux下的shell运算(加、减、乘、除)

    关注微信公众号 虾米聊吧 获取所有资料干货 xff0c 每天更新技术干货 xff0c 一起交流一起学习 i 61 j 43 k 等价于 i 61 96 expr j 43 k 96 i 61 j k 等价于 i 61 96 expr j k
  • 仿QQ聊天程序(java)

    简易版qq聊天 xff1a qq聊天 简易版 resourcecode cn 推荐java最新聊天项目 xff08 java仿微信聊天 xff09 java 简单仿微信聊天 springboot Garry1115的博客 CSDN博客 sp
  • yum出错Error: Cannot find a valid baseurl for repo: base

    关注微信公众号 虾米聊吧 xff0c 每天更新一篇技术文章 xff0c 文章内容涵盖架构师成长必经之路应掌握的技术 xff0c 一起学习 xff0c 一起交流 最近在安装mysql的rpm包时 xff0c 出现了一个问题 xff0c 当使用

随机推荐

  • java web简单权限管理设计

    注 xff1a 由于该项目比较老 xff0c 所以没有采用maven管理 xff0c 建议下载java后台通用权限管理系统 springboot xff09 xff0c 对学习和使用会更有帮助 最近在做一个网站类型项目 xff0c 主要负责
  • C/C++ 开发神器 CLion 使用入门

    关注微信公众号 虾米聊吧 xff0c 每天分享知识干货 xff0c 和博主一起打卡 xff0c 进步 CLion是Jetbrains公司旗下新推出的一款专为开发C C 43 43 所设计的跨平台IDE xff0c 它是以IntelliJ为基
  • java后台通用权限管理系统(springboot)

    推荐 xff1a Java秒杀系统优化 高性能高并发 xff08 Java秒杀系统优化 高性能高并发 Garry1115的博客 CSDN博客 xff09 说明 xff1a 这是本人正在使用的一款通用权限管理系统 来源 xff1a 通过对网上
  • Kali Linux-2021.4a下载安装全过程

    一 镜像下载 xff1b 下载镜像地址通过阿里云开源镜像站进行下载 xff1b https mirrors aliyun com kali images 二 系统安装 xff1b 说明 xff1a 本次安装流程通过虚拟机进行 1 虚拟机配置
  • RH8的ansible安装与配置

    Ansible安装与配置 自定义环境 角色主机名ip地址组名控制主机server example com192 168 157 100server受控主机1node1 example com192 168 157 134node1受控主机2
  • UOS开发者调试签名

    开发者调试签名 2022 01 18 20 03 03 一 证书生成 xff08 1 xff09 打开统信应用商店 xff0c 搜索 证书工具 xff0c 单击安装证书工具 xff08 2 xff09 使用cert tool工具生成证书 执
  • JAVA 分解质因数

    7 3 分解质因数 求出区间 a b 中所有整数的质因数分解 输入格式 输入两个整数a xff0c b 数据规模和约定 2 lt 61 a lt 61 b lt 61 10000 输出格式 每行输出一个数的分解 xff0c 形如k 61 a
  • 使用customRef自定义ref,解决setup中处理异步问题。

    setup中不允许使用async await使用customRef可以让请求到的数据自动获取响应式状态详见下方demo lt template gt lt div gt num lt div gt lt button 64 click 61
  • apache2.4 中文乱码问题

    PHP ini里面的default charset 61 34 UTF 8 34 lt meta http equiv 61 34 content type 34 content 61 34 text html charset 61 UTF
  • VScode自定义配置代码片段详细教程(附带转码链接)

    目录 前言 1 定义自己常用的代码片段 2 通过转码链接进转译 3 Vscode中设置 3 1找到配置代码片段进行设置 3 2点击全局配置进入粘贴转译过的代码 4 检验自定义代码段是否成功 小结 xff1a 前言 众所周知在VScode的h
  • 注意力机制总结

    文章目录 1 通道注意力1 1 SENet xff08 谁用谁知道 xff0c 用了都说好 xff09 2 通道 amp 空间2 1 CBAM2 2 scSE2 3 Coordinate Attention 3 self attention
  • 轻量级网络总结

    文章目录 1 SqueezeNet2 ShuffleNet2 1 v12 2 v2 3 MobileNet3 1 v13 2 v23 3 v3 4 GhostNet4 1 v14 2 v2 1 SqueezeNet SqueezeNet A
  • 【低光增强】Zero-DCE

    文章目录 一 前言二 算法理解2 1 低光增强曲线2 2 整体框架2 3 网络结构2 4 损失函数2 4 1 空间一致性2 4 2 曝光控制2 4 3 色彩恒常2 4 4 光照平滑 2 5 Zero DCE 43 43 三 效果测试 一 前
  • 【对比度增强】Learning Tone Curves for Local Image Enhancement(LTMNet)

    文章目录 0 前言1 理解1 1 整体框架1 2 网络结构1 3 细节 2 亮点3 总结 0 前言 LTMNet这篇文章借鉴了CLAHE算法 xff0c 所有步骤与CLAHE一致 xff0c 不同之处在于LTMNet中局部映射曲线是通过CN
  • 【数字图像处理】边缘检测

    文章目录 0 前言1 Sobel算子2 Canny算子3 深度学习算法3 1 Holistically Nested Edge Detection xff08 HED xff09 3 2 Richer Convolutional Featu
  • 语义分割总结

    文章目录 0 前言1 数据集2 经典网络2 1 FCN2 2 U Net2 3 DeepLab2 4 PSPNet2 5 SegNet2 6 CCNet2 7 SegFormer 3 损失函数4 评价指标5 最新进展 xff08 2023
  • 使用matlab绘制世界地图并根据经纬度绘制点位(附m_map的下载与安装说明)

    文章目录 1 worldmap amp geoshow2 m map工具箱3 根据经纬度在世界地图上绘制点位 使用matlab绘制世界地图有两种方法 xff08 自己使用过的 xff0c 可能有别的我不了解的方法 xff09 xff1a 第
  • C 语言使用宏自定义可打印的枚举(enum) 类型

    1 前言 xff1a 说点废话 xff0c 时间紧的请直接跳过 xff0c 看后面的实现 尽管本人很反感 C 语言中的宏定义 xff0c 特别是滥用宏定义经常会让问题变的扑朔迷离 xff0c 但是不得不承认 xff0c 在某些时候 xff0
  • Matlab GUI设计之坐标转换(附Matlab GUI设计学习手册完整版pdf)

    文章目录 如何开始 xff1f 1 界面布局2 编写回调函数 相信看这篇文章的你们大部分没有用Matlab做过界面设计 xff0c 其实不只是你们 xff0c 我也是第一次 xff08 手动滑稽 xff09 xff0c 在此将我的经验同大家
  • 【Linux】线程互斥

    目录 1 进程线程间的互斥相关背景概念 2 互斥量mutex 2 1 基本概念 2 2 售票系统举例 2 3 解释 3 互斥量的接口 3 1 初始化互斥量 3 1 1 静态分配 3 1 2 动态分配 xff08 pthread mutex