Linux线程同步与互斥

?linux线程互斥

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

  如果不能保持互斥,那么会发生一些不合逻辑的事情,以下面这段多线程抢票代码为例:

代码语言:javascript代码运行次数:0运行复制

#include #include #include "thread.hpp" // 自己实现的线程封装using namespace ThreadModule;// 数据不一致int g_tickets = 10000; // 共享资源,没有保护的, 多线程同时访问void route(int &tickets){    while (true)    {        if(tickets>0) // 票数小于0, 终止抢票        {            usleep(1000);            printf("get tickets: %dn", tickets);            tickets--;        }        else        {            break;        }    }}const int num = 4;int main(){    // std::cout << "main: &tickets: " << &g_tickets << std::endl;    std::vector<Thread> threads;    // 1. 创建一批线程    for (int i = 0; i < num; i++)    {        std::string name = "thread-" + std::to_string(i + 1);        threads.emplace_back(route, g_tickets, name);    }    // 2. 启动 一批线程    for (auto &thread : threads)    {        thread.Start();    }    // 3. 等待一批线程    for (auto &thread : threads)    {        thread.Join();        std::cout << "wait thread done, thread is: " << thread.name() << std::endl;    }    return 0;}
Linux线程同步与互斥

  这里线程对同一个共享资源进行操作,进行并发执行类似 “抢票” 的模式,但是最后得到的数据却发现,抢票居然还有负数?这种情况我们称为 数据不一致。

  这个问题是怎么产生怎么导致的呢?首先我们先要了解一个概念:原子性,前面我们说,原子性只有两态,要么已完成,要么未完成。实际上,在编程的角度来说,原子性指的是汇编层面只有一条语句。比如对一个内置类型进行赋值操作,在汇编层面其实就是一条move指令。所以其是原子的。

  了解了上述概念之后,我们再来看一看代码的逻辑结构,在route函数里,我们对tickets进行了判断,而判断是逻辑运算,需要在CPU内进行操作。

立即进入“豆包AI人工智官网入口”;

立即学习“豆包AI人工智能在线问答入口”;

Linux线程同步与互斥

  判断完成后,刚刚进入内部,执行usleep()函数,所以此时线程就被切换,进入到等待队列。假设此时是thread-1在跑,又因为tickets被保存到寄存器当中,而thread-1此时要进行线程切换则需要带走thread-1的数据,则此时thread-1把寄存器中的tickets带走了。(线程等待结束后才会继续执行后续代码)

  随后thread-2也开始执行此函数,因为上一个thread-1线程遇到了usleep,所以后续的tickets- -,以及total- – 都是没有执行的。也就是说上一次对tickets操作后tickets值并没有变,所以此时thread-2同样将内存中的tickets加载到寄存器当中,同样,tickets此时的值还是1,同样thread-2遇到usleep,那么thread-2也要带着自己的数据到等待队列当中。

Linux线程同步与互斥

  把全局变量加载到CPU不是本质,本质是 将共享的全局变量加载到寄存器使得当前线程私有化共享全局变量。而此时寄存器的值又没有被写回,所以此时thread-2也进入到等待队列。同理,周而复始,thread-3 4都是如此。

  而当等待队列等待完成后,所有线程都开始执行后续的代码,之前阻塞到printf,这里printf不影响tickets所以忽略。后续代码执行到 tickets-- ⇒ tickets = tickets - 1; 此步操作不是原子的,因为它需要经历:1、从内存读取到CPU. 2、CPU内部进行- -操作. 3、写回到内存中, 那么此时问题就出来了。

  当thread-1已经将tickets进行了–,并且将其写回到了内存。那么接下来thread-2等待完成又对tickets进行–,此时CPU中的tickets已经变为0了,所以–过后tickets变为了-1,再次将其写回到内存当中。周而复始,使得原本正常的抢票,最后却变为了负数。

?线程锁

  上面我们已经把问题给搞明白了,接下来我们需要解决问题,如何解决这种线程问题呢?通常的解决方案是对线程进行加锁。

✈️互斥量Mutex

  大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互。多个线程并发的操作共享变量,会带来一些问题。为了解决上述问题:

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

  为此,Linux给我们提供了互斥锁,首先我们先来认识一下这些接口:

Linux线程同步与互斥

?初始化互斥量的两种方式

  使用 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 宏进行初始化互斥量,那么这把锁就可以直接使用了。

代码语言:javascript代码运行次数:0运行复制

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

函数参数:

mutex:要初始化的互斥量 attr:NULL?申请锁方式

  不论我们使用哪种方式定义上面的锁,我们都可以对这把锁进行上锁 pthread_lock():

Linux线程同步与互斥

申请锁接口:

代码语言:javascript代码运行次数:0运行复制

int pthread_mutex_lock(pthread_mutex_t *mutex);

  使用该接口只有三种结果:

申请成功,函数会返回,允许继续向后执行。申请失败,函数会阻塞,不允许向后运行。函数调用失败,出错返回。

尝试申请锁:

代码语言:javascript代码运行次数:0运行复制

int pthread_mutex_trylock(pthread_mutex_t *mutex);

  尝试申请锁,与pthread_mutex_lock()唯一不同的是,当申请锁失败之后,不会进行阻塞等待,而是直接出错返回,并设置错误码返回出错原因。

?解除与销毁锁

解除互斥锁:

代码语言:javascript代码运行次数:0运行复制

int pthread_mutex_unlock(pthread_mutex_t *mutex);

  有加锁必然有解锁,当线程在临界资源内执行完毕后,需要释放当前锁,让其他线程进入,所以需要释放锁。

销毁互斥锁:

代码语言:javascript代码运行次数:0运行复制

int pthread_mutex_destroy(pthread_mutex_t *mutex);

使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁不要销毁一个已经加锁的互斥量已经销毁的互斥量,要确保后面不会有线程再尝试加锁

✈️问题解决及线程饥饿

  出现数据不一致问题的本质是,多个执行流并发访问全局数据的代码所导致的!访问公共资源的代码,我们称为临界区。

  我们加锁的本质是把并行的执行流改变为串行的执行流,而对临街资源的保护实质上就是对临街区代码的加解锁。

代码语言:javascript代码运行次数:0运行复制

#include #include #include "thread.hpp"using namespace ThreadModule;// 数据不一致int g_tickets = 10000; // 共享资源,没有保护的, 多线程同时访问class ThreadData{public:    ThreadData(int tickets, const std::string &name):_tickets(tickets), _name(name), _total(0)    {}    ~ThreadData()    {}public:    int &_tickets; // 所有的线程最后都会引用同一个全局的g_tickets    std::string _name;    int _total;};pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;// 创建互斥量void route(ThreadData *td){    while (true)    {        // 加锁力度越细越好        pthread_mutex_lock(&gmutex);// 上锁        if(td->_tickets>0)        {            usleep(1000);            printf("%s running, get tickets: %dn",td->_name.c_str(),  td->_tickets);            td->_tickets--;            pthread_mutex_unlock(&gmutex);// 解锁            td->_total++;// 将解锁放在此句后面也是可以的,只不过这里的total已经不属于临界区了,所以如果要严格按照规则加锁解锁,就在上一句进行解锁        }        else        {            break;        }    }}const int num = 4;// 创建线程数int main(){    // std::cout << "main: &tickets: " << &g_tickets << std::endl;    std::vector<Thread> threads;    std::vector datas;    // 1. 创建一批线程    for (int i = 0; i < num; i++)    {        std::string name = "thread-" + std::to_string(i + 1);        ThreadData* td = new ThreadData(g_tickets, name);        threads.emplace_back(route, td, name);        datas.emplace_back(td);    }    // 2. 启动 一批线程    for (auto &thread : threads)    {        thread.Start();    }    // 3. 等待一批线程    for (auto &thread : threads)    {        thread.Join();        // std::cout << "wait thread done, thread is: " << thread.name() << std::endl;    }    // 4. 输出统计数据    for(auto & data:datas)    {        std::cout <_name << " : " <_total << std::endl;        delete data;    }    return 0;}
Linux线程同步与互斥

  这样加锁了之后,就不会再出现之前的情形,数据也就正常了。但是如果你是CentOS的用户的话,是有一些bug的,因为CentOS环境中,某些线程的竞争能力太强了,以至于得到的结果往往只有一个线程有结果,其他线程为0,这是因为在CentOS中对线程调度的算法没有Unbuntu的新,也就是没有Ubuntu的算法好。

  所以,又能得出另一个结论:多线程加锁,这些多线程对锁的竞争是自由的。如果竞争能力太强的线程,会导致其他线程抢不到锁,也就造成了线程饥饿问题!我所说的CentOS的这种行为就是竞争饥饿问题。

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

Linux线程同步与互斥

  swap或者exchange可以交换寄存器和内存单元中的值,第一句movb 0, al,把左值赋值给寄存器al,第二步xchgb把寄存器内的值和Mutex锁进行交换,随后判断 寄存器内的内容是否>0,如果是则返回0,表示加锁成功,否则就挂起等待, 表示当前锁被线程等待,等待完成继续执行锁。

  解锁的过程,此时线程已经执行完毕,把寄存器中的值重新放进内存的mutex变量中,表示当前锁已经释放。下图或许能帮助你更好的理解这一过程:

Linux线程同步与互斥

  为什么线程能做这件事呢?我们之前说过,CPU寄存器内部的数据,保存了线程的硬件上下文,而数据在内存里,所有线程都能够访问,属于共享的,但是如果转移到CPU内部的寄存器中,就属于一个线程私有了。

  上图中,线程1因为某些原因需要线程切换,进入等待队列。那么此时线程1需要把自己的上下文数据带走,其实就是把寄存器当中保存的值带走,并且没有对内存交换的0进行写回,也就是说此时内存中的mutex是0,那么线程2在交换mutex到寄存器当中,就会进行状态检测,此时检测到状态为0,说明当前已经有人占用锁了,则线程2进入到挂起状态,后来的线程依旧会如此,直到第一个线程执行完毕将锁释放。

  所以上述所谓的交换就显得尤为重要,这里的交换指的不是单纯的拷贝,而是所有线程在争锁的时候只有一个值,而这个值往往就是那把锁。交换过程只有一条汇编语句,所以 交换过程是原子的,那么就能保证交换时不会发生线程切换这样的事情。

  一个线程在访问临界区时,对于其他线程来说,1、锁被释放。2、曾经没有申请到锁正在挂起状态。此时当前线程访问临街资源是加了锁的,对其他线程来说这一过程是原子的,所以说此时访问临界区资源是线程安全的。

?线程同步

  主线开始前,我们先来听一个故事:

Linux线程同步与互斥

但是呢这个时候阿飞回头一看,阿飞刚出电话亭,就看到密密麻麻站满了人,“这吃完饭再来不得到猴年马月才能打上电话?” 于是阿飞咬咬牙,大不了中午不吃饭了,说完,因为他距离门最近,他又进去把门关了,又叙了两个小时。随后阿飞痛快的出门,可是刚出门就想起来自己没生活费了,然后又急忙转身进入电话亭把门关上,又给家里打了电话。这样来来回回好几次,一直占着电话亭。 此时电话亭外面的人不乐意了,“怎么还xx的不出来,再不出来劳资见你一次打你一次!”,阿飞眼看着局势不对,也不敢出电话亭,于是就拨通了警察局的电话,警察来了之后,了解了大致情况。于是在电话亭这里设立了警戒线,并且装上了高清摄像头,并规定:每个人来到这里以后必须要排队,并且打完电话的人不能再次直接进入,必须从队尾重新排队打电话。

  其实上面这个故事就是今天的主线,线程同步,为什么这么说呢?我们把人比作线程,在警察来之前,线程一直在占用这个锁,导致其他线程没办法拿到锁,一直处于等待状态,就会产生线程饥饿问题。而第二种情况,每个线程在没拿到锁之前都需要排队等待,并且拿到锁的线程如果要二次进入则需要重新到队尾排队。

  而上述的过程基本上做到了让不同线程在保证电话亭安全的前提下,让所有的线程访问临界资源具有了一定的顺序性。这个工作我们称为 线程同步。

同步:在保证 数据安全 的前提下,让线程能够按照某种特定的顺序访问 临界资源,从而有效避免 饥饿问题,叫做 同步。

✈️条件变量

  实现线程同步,我们常用做法是使用条件变量。这里的条件变量可不是环境变量,那什么是条件变量呢?

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。

  例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

✈️条件变量函数

条件变量初始化(动态,局部条件变量):

代码语言:javascript代码运行次数:0运行复制

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrictattr);

函数参数:

cond:要初始化的条件变量 attr:NULL

静态,全局条件变量初始化:

代码语言:javascript代码运行次数:0运行复制

pthread_cond_t cond cond = PTHREAD_COND_INTIALIZER;

  这里与互斥锁规则相似,不再过多赘述。

销毁条件变量:

代码语言:javascript代码运行次数:0运行复制

int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满足:

代码语言:javascript代码运行次数:0运行复制

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

函数参数:

cond:要在这个条件变量上等待(队列内等待)mutex:互斥量

唤醒线程:

代码语言:javascript代码运行次数:0运行复制

int pthread_cond_broadcast(pthread_cond_t *cond);// 唤醒所有在cond等待下的线程int pthread_cond_signal(pthread_cond_t *cond);// 唤醒一个线程

  以上接口的返回值,全部都是:返回0为成功,失败设置错误码。要学习条件变量实际上上面这些接口就足够了。

✈️条件变量示例

  这里使用全局条件变量,全部使用接口调用的形式展示条件变量的作用: 创建一个主控线程,3个附属线程,对三个附属线程进行cond等待,通过主控线程唤醒这些线程(全部唤醒和单独唤醒)。

  main函数内定义一个接收tid的数组,一函数调用的形式分别创建一个主控线程和多个附属线程:

代码语言:javascript代码运行次数:0运行复制

int main(){    std::vector tids;    StartMaster(&tids);// 主控线程    StartSlaver(&tids);// 其他线程    WaitThread(tids);// 线程等待    return 0;}

  添加需要的头文件,以及设置全局条件变量与全局互斥锁:

代码语言:javascript代码运行次数:0运行复制

#include #include #include #include #include pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;// 全局条件变量pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;// 全局互斥量

  创建附属线程,默认创建3个附属线程,所有附属线程执行同一回调SlaverCore,回调内将所有线程在临界区内加锁并等待,此时线程锁gmutex释放 线程进入cond等待队列,等待主控线程唤醒,下一个线程重复此步操作,直至所有现成进入到cond等待队列,等待主控唤醒:

代码语言:javascript代码运行次数:0运行复制

void* SlaverCore(void* args){    std::string name = static_cast(args);    while(true)    {        // 1. 加锁        pthread_mutex_lock(&gmutex);        // 2. 一般条件变量是在加锁和解锁之间使用        pthread_cond_wait(&gcond, &gmutex);// gmutex: 这个是用来释放的[前一半],进入等待队列,此时锁被释放        std::cout << "当前被叫醒的线程是:" << name << std::endl;        pthread_mutex_unlock(&gmutex);    }    return nullptr;}void StartSlaver(std::vector *tidsptr, int threadnum = 3){    for(int i = 0; i < threadnum; ++i)    {        char *name = new char[64];// 每一个线程都需要new 一个新名字,否则很可能会出现线程覆盖问题        snprintf(name, 64, "slaver-%d", i + 1);        pthread_t tid;        int n = pthread_create(&tid, nullptr, SlaverCore, name);        if(n == 0)        {            std::cout << "create sucess: " << name <emplace_back(tid);        }    }}

  主控线程,创建主控线程,执行主控回调,主控回调函数内,休眠三秒确保所有附属线程进入等待队列,在循环里可选择的将所有线程选择全部唤醒或者隔一秒唤醒一个线程:

代码语言:javascript代码运行次数:0运行复制

void* MasterCore(void *args)// call back func{    sleep(3);    std::cout << "master start work..." << std::endl;    std::string name = static_cast(args);    while(true)    {        // pthread_cond_signal(&gcond);// 一次唤醒1个线程        pthread_cond_broadcast(&gcond);// 广播唤醒所有的线程        std::cout << "master awake a thread..." << std::endl;        sleep(1);    }    return nullptr;}void StartMaster(std::vector *tidsptr)// main contrl thread{    pthread_t tid;    int n = pthread_create(&tid, nullptr, MasterCore, (void*)"Master Thread");    if(n == 0)    {        std::cout << "create master success" <emplace_back(tid);}

  main-thread阻塞等待回收所有线程:

代码语言:javascript代码运行次数:0运行复制

void WaitThread(std::vector &tids){    for(auto & tid : tids)    {        pthread_join(tid, nullptr);    }}

主控线程一次性全部唤醒等待队列的线程:

Linux线程同步与互斥

主控线程每隔一秒唤醒一个线程:

Linux线程同步与互斥

以上就是Linux线程同步与互斥的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/12239.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月19日 02:29:23
下一篇 2025年11月19日 03:01:02

相关推荐

  • 怎样用免费工具美化PPT_免费美化PPT的实用方法分享

    利用KIMI智能助手可免费将PPT美化为科技感风格,但需核对文字准确性;2. 天工AI擅长优化内容结构,提升逻辑性,适合高质量内容需求;3. SlidesAI支持语音输入与自动排版,操作便捷,利于紧急场景;4. Prezo提供多种模板,自动生成图文并茂幻灯片,适合学生与初创团队。 如果您有一份内容完…

    2025年12月6日 软件教程
    000
  • Pages怎么协作编辑同一文档 Pages多人实时协作的流程

    首先启用Pages共享功能,点击右上角共享按钮并选择“添加协作者”,设置为可编辑并生成链接;接着复制链接通过邮件或社交软件发送给成员,确保其使用Apple ID登录iCloud后即可加入编辑;也可直接在共享菜单中输入邮箱地址定向邀请,设定编辑权限后发送;最后在共享面板中管理协作者权限,查看实时在线状…

    2025年12月6日 软件教程
    100
  • REDMI K90系列正式发布,售价2599元起!

    10月23日,redmi k90系列正式亮相,推出redmi k90与redmi k90 pro max两款新机。其中,redmi k90搭载骁龙8至尊版处理器、7100mah大电池及100w有线快充等多项旗舰配置,起售价为2599元,官方称其为k系列迄今为止最完整的标准版本。 图源:REDMI红米…

    2025年12月6日 行业动态
    200
  • Linux中如何安装Nginx服务_Linux安装Nginx服务的完整指南

    首先更新系统软件包,然后通过对应包管理器安装Nginx,启动并启用服务,开放防火墙端口,最后验证欢迎页显示以确认安装成功。 在Linux系统中安装Nginx服务是搭建Web服务器的第一步。Nginx以高性能、低资源消耗和良好的并发处理能力著称,广泛用于静态内容服务、反向代理和负载均衡。以下是在主流L…

    2025年12月6日 运维
    000
  • Linux journalctl与systemctl status结合分析

    先看 systemctl status 确认服务状态,再用 journalctl 查看详细日志。例如 nginx 启动失败时,systemctl status 显示 Active: failed,journalctl -u nginx 发现端口 80 被占用,结合两者可快速定位问题根源。 在 Lin…

    2025年12月6日 运维
    100
  • 华为新机发布计划曝光:Pura 90系列或明年4月登场

    近日,有数码博主透露了华为2025年至2026年的新品规划,其中pura 90系列预计在2026年4月发布,有望成为华为新一代影像旗舰。根据路线图,华为将在2025年底至2026年陆续推出mate 80系列、折叠屏新机mate x7系列以及nova 15系列,而pura 90系列则将成为2026年上…

    2025年12月6日 行业动态
    100
  • Linux如何防止缓冲区溢出_Linux防止缓冲区溢出的安全措施

    缓冲区溢出可通过栈保护、ASLR、NX bit、安全编译选项和良好编码实践来防范。1. 使用-fstack-protector-strong插入canary检测栈破坏;2. 启用ASLR(kernel.randomize_va_space=2)随机化内存布局;3. 利用NX bit标记不可执行内存页…

    2025年12月6日 运维
    000
  • Linux如何优化系统性能_Linux系统性能优化的实用方法

    优化Linux性能需先监控资源使用,通过top、vmstat等命令分析负载,再调整内核参数如TCP优化与内存交换,结合关闭无用服务、选用合适文件系统与I/O调度器,持续按需调优以提升系统效率。 Linux系统性能优化的核心在于合理配置资源、监控系统状态并及时调整瓶颈环节。通过一系列实用手段,可以显著…

    2025年12月6日 运维
    000
  • Pboot插件数据库连接的配置教程_Pboot插件数据库备份的自动化脚本

    首先配置PbootCMS数据库连接参数,确保插件正常访问;接着创建auto_backup.php脚本实现备份功能;然后通过Windows任务计划程序或Linux Cron定时执行该脚本,完成自动化备份流程。 如果您正在开发或维护一个基于PbootCMS的网站,并希望实现插件对数据库的连接配置以及自动…

    2025年12月6日 软件教程
    000
  • Linux命令行中wc命令的实用技巧

    wc命令可统计文件的行数、单词数、字符数和字节数,常用-l统计行数,如wc -l /etc/passwd查看用户数量;结合grep可分析日志,如grep “error” logfile.txt | wc -l统计错误行数;-w统计单词数,-m统计字符数(含空格换行),-c统计…

    2025年12月6日 运维
    000
  • Linux命令行中fc命令的使用方法

    fc 是 Linux 中用于管理命令历史的工具,可查看、编辑并重新执行历史命令。输入 fc 直接编辑最近一条命令,默认调用 $EDITOR 打开编辑器修改后自动执行;通过 fc 100 110 或 fc -5 -1 可批量编辑指定范围的历史命令,保存后按序重跑;使用 fc -l 列出命令历史,支持起…

    2025年12月6日 运维
    000
  • 曝小米17 Air正在筹备 超薄机身+2亿像素+eSIM技术?

    近日,手机行业再度掀起超薄机型热潮,三星与苹果已相继推出s25 edge与iphone air等轻薄旗舰,引发市场高度关注。在此趋势下,多家国产厂商被曝正积极布局相关技术,加速抢占这一细分赛道。据业内人士消息,小米的超薄旗舰机型小米17 air已进入筹备阶段。 小米17 Pro 爆料显示,小米正在评…

    2025年12月6日 行业动态
    000
  • 荣耀手表5Pro 10月23日正式开启首销国补优惠价1359.2元起售

    荣耀手表5pro自9月25日开启全渠道预售以来,市场热度持续攀升,上市初期便迎来抢购热潮,一度出现全线售罄、供不应求的局面。10月23日,荣耀手表5pro正式迎来首销,提供蓝牙版与esim版两种选择。其中,蓝牙版本的攀登者(橙色)、开拓者(黑色)和远航者(灰色)首销期间享受国补优惠价,到手价为135…

    2025年12月6日 行业动态
    000
  • VSCode终端美化:功率线字体配置

    首先需安装Powerline字体如Nerd Fonts,再在VSCode设置中将terminal.integrated.fontFamily设为’FiraCode Nerd Font’等支持字体,最后配合oh-my-zsh的powerlevel10k等Shell主题启用完整美…

    2025年12月6日 开发工具
    000
  • Linux命令行中locate命令的快速查找方法

    locate命令通过查询数据库快速查找文件,使用-i可忽略大小写,-n限制结果数量,-c统计匹配项,-r支持正则表达式精确匹配,刚创建的文件需运行sudo updatedb更新数据库才能查到。 在Linux命令行中,locate 命令是快速查找文件和目录路径的高效工具。它不直接扫描整个文件系统,而是…

    2025年12月6日 运维
    000
  • 环境搭建docker环境下如何快速部署mysql集群

    使用Docker Compose部署MySQL主从集群,通过配置文件设置server-id和binlog,编写docker-compose.yml定义主从服务并组网,启动后创建复制用户并配置主从连接,最后验证数据同步是否正常。 在Docker环境下快速部署MySQL集群,关键在于合理使用Docker…

    2025年12月6日 数据库
    000
  • Linux文件系统rsync命令详解

    rsync通过增量同步高效复制文件,支持本地及远程同步,常用选项包括-a、-v、-z和–delete,结合SSH可安全传输数据,配合cron可实现定时备份。 rsync 是 Linux 系统中一个非常强大且常用的文件同步工具,能够高效地在本地或远程系统之间复制和同步文件与目录。它以“增量…

    2025年12月6日 运维
    000
  • Linux systemctl list-dependencies命令详解

    systemctl list-dependencies 用于查看 systemd 单元的依赖关系,帮助排查启动问题和优化启动流程。1. 基本语法为 systemctl list-dependencies [选项] [单元名称],默认显示 default.target 的依赖。2. 常见单元类型包括 …

    2025年12月6日 运维
    000
  • RTX 5090性能怪兽!雷蛇灵刃18 2025游戏本图赏

    10月25日,雷蛇正式推出全新灵刃18 2025款旗舰级游戏笔记本,首发搭载nvidia rtx 50系列显卡,起售价为25999元。 目前该机型已抵达评测室,以下为实机图赏。 新款灵刃18配备一块18英寸双模屏幕,支持UHD+ 240Hz与FHD+ 440Hz两种显示模式,响应时间最快可达3ms。…

    2025年12月6日 行业动态
    000
  • Xbox删忍龙美女角色 斯宾塞致敬板垣伴信被喷太虚伪

    近日,海外游戏推主@HaileyEira公开发表言论,批评Xbox负责人菲尔·斯宾塞不配向已故的《死或生》与《忍者龙剑传》系列之父板垣伴信致敬。她指出,Xbox并未真正尊重这位传奇制作人的创作遗产,反而在宣传相关作品时对内容进行了审查和删减。 所涉游戏为年初推出的《忍者龙剑传2:黑之章》,该作采用虚…

    2025年12月6日 游戏教程
    000

发表回复

登录后才能评论
关注微信