【Qt】Qt的线程(两种QThread类的详细使用方式)

article/2025/10/16 8:51:55

Qt提供QThread类以进行多任务处理。与多任务处理一样,Qt提供的线程可以做到单个线程做不到的事情。例如,网络应用程序中,可以使用线程处理多种连接器。

QThread继承自QObject类,且提供QMutex类以实现同步。线程和进程共享全局变量,可以使用互斥体对改变后的全局变量值实现同步。因此,必须编辑全局数据时,使用互斥体实现同步,其它进程则不能改变或浏览全局变量值。

什么是互斥体?

互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。

在任意时刻,只有一个线程被允许进入代码保护区。任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。

什么时候需要使用互斥体呢?

互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。

 

Qt线程基础

QThread的创建和启动

class MyThread : public QThread
{Q_OBJECT
protected:void run();
};void MyThread :: run(){...
}

如上述代码所示,如果要创建线程,则必须继承QThread类。MyThread使用成员函数run()才会实现线程。

Qt提供的线程类

Qt提供的线程类
线程类说明
QAtomicInt提供了Integer上与平台无关的Qtomic运算
QAtomicPointer提供了指针上Atomic运算的模板函数
QFuture显示异步运算结果的类
QFutureSynchronizerQFuture类简化同步而提供的类
QFutureWatcher使用信号和槽,允许QFuture监听
QMutex访问类之间的同步
QMutecLocker简化Lock和Unlock Mutex的类
QReadWriteLock控制读写操作的类
QReadLocker为了读访问而提供的
QWriteLocker为了写访问而提供的
QRunnable正在运行的所有对象的父类,且定义了虚函数run()
QSemaphore一般的Count互斥体类
QThread提供与平台无关的线程功能的类
QThreadPool管理线程的类
QThreadStorage提供每个线程存储区域的类
QWaitCondition确认线程间同步的类的状态值

同步QThread的类

为了同步线程,Qt提供了QMutex、QReadWriteLock、QSemaphore和QWaitCondition类。主线程等待与其他线程的中断时,必须进行同步。例如:两个线程同时访问共享变量,那么可能得不到预想的结果。因此,两个线程访问共享变量时,必须进行同步。

  1. 一个线程访问指定的共享变量时,为了禁止其他线程访问,QMutex提供了类似锁定装置的功能。互斥体激活状态下,线程不能同时访问共享变量,必须在先访问的线程完成访问后,其他线程才可以继续访问。
  2. 一个线程访问互斥体锁定的共享变量期间,如果其他线程也访问此共享变量,那么该线程将会一直处于休眠状态,直到正在访问的线程结束访问。这称为线程安全。
  3. QReadWriteLock和QMutex的功能相同,区别在于,QReadWriteLock对数据的访问分为读访问和写访问。很多线程频繁访问共享变量时,与QMetex相对,使用QReadWriteLock更合适。
  4. QSemaphore拥有和QMutex一样的同步功能,可以管理多个按数字识别的资源。QMutex只能管理一个资源,但如果使用QSemaphore,则可以管理多个按号码识别的资源。
  5. 条件符合时,QWaitCondition允许唤醒线程。例如,多个线程中某个线程被阻塞时,通过QWaitCondition提供的函数wakeOne()和wakeAll()可以唤醒该线程。

可重入性与线程安全

  • 可重入性:两个以上线程并行访问时,即使不按照调用顺序重叠运行代码,也必须保证结果;
  • 线程安全:线程并行运行的情况下,虽然保证可以使程序正常运行,但访问静态空间或共享(堆等内存对象)对象时,要使用互斥体等机制保证结果。

一个线程安全的函数不一定是可重入的;一个可重入的函数缺也不一定是线程安全的!

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个线程调用此函数时,很有可能使有关全局变量变为不可知状态。

满足下列条件的函数多数是不可重入的:

  • 函数体内使用了静态的数据结构和全局变量,若必须访问全局变量,利用互斥信号量来保护全局变量;;
  • 函数体内调用了malloc()或者free()函数;
  • 函数体内调用了标准I/O函数。

常见的不可重入函数有:

  • printf --------引用全局变量stdout
  • malloc --------全局内存分配表
  • free    --------全局内存分配表

也就是说:本质上,可重入性与C++类或者没有全局静态变量的函数相似,由于只能访问自身所有的数据变量区域,所以即使有两个以上线程访问,也可以保证安全性。

QThread和QObjects

QThread类继承自QObjects类。因此,线程开始或结束时,QThread类发生发送信号事件。信号与槽的功能是QThread类从QObject类继承的,可以通过信号与槽处理开始或结束等操作,所以可以实现多线程。QObject是基于QTimer、QTcpSocket、QUdpSocket和QProcess之类的非图形用户界面的子类。

基于非图形用户界面的子类可以无线程操作。单一类运行某功能时,可以不需要线程。但是,运行单一类的目标程序的上级功能时,则必须通过线程实现。

线程A和线程B没有结束的情况下,应设计使主线程时间循环不结束;而若线程A迟迟不结束而导致主线程循环也迟迟不能结束,故也要防止线程A没有在一定时间内结束。

处理QThread的信号和槽的类型

Qt提供了可以决定信号与槽类型的枚举类,以在线程环境中适当处理事物。

决定信号与槽类型的枚举类
常量说明
Qt::AutoConnection0如果其他线程中发生信号,则会插入队列,像QueuedConnection一样,否则如DirectConnection一样,直接连接到槽。发送信号时决定Connection类型。
Qt::DirectConnection1发生信号事件后,槽立即响应
Qt::QueuedConnection2返回收到的线程事件循环时,发生槽事件。槽在收到的线程中运行
Qt::BlockingQueuedConnection3与QueuedConnection一样,返回槽时,线程被阻塞。建立在事件发生处使用该类型

使用QtConcurrent类的并行编程

QtConcurrent类提供多线程功能,不使用互斥体、读写锁、等待条件和信号量等低级线程。使用QtConcurrent创建的程序会根据进程数自行调整使用的线程数。

 

QThread类

简述

QThread类提供了与系统无关的线程。

QThread代表在程序中一个单独的线程控制。线程在run()中开始执行,默认情况下,run()通过调用exec()启动事件循环并在线程里运行一个Qt的事件循环。

详细描述

QThread类可以不受平台影响而实现线程。QThread提供在程序中可以控制和管理线程的多种成员函数和信号/槽。通过QThread类的成员函数start()启动线程。

QThread通过信号函数started()和finished()通知开始和结束,并查看线程状态;可以使用isFinished()和isRunning()来查询线程的状态;使用函数exit()和quit()可以结束线程。

如果使用多线程,有时需要等到所有线程终止。此时,使用函数wait()即可。线程中,使用成员函数sleep()、msleep()和usleep()可以暂停秒、毫秒及微秒单位的线程。

一般情况下,wait()和sleep()函数应该不需要,因为Qt是一个事件驱动型框架。考虑监听finished()信号来取代wait(),使用QTimer来取代sleep()。

静态函数currentThreadId()和currentThread()返回标识当前正在执行的线程。前者返回该线程平台特定的ID,后者返回一个线程指针。

要设置线程的名称,可以在启动线程之前调用setObjectName()。如果不调用setObjectName(),线程的名称将是线程对象的运行时类型(QThread子类的类名)。

线程管理

可以将常用的接口按照功能进行以下分类:

线程启动

void start(Priority priority = InheritPriority) [slot] 

调用后会执行run()函数,但在run()函数执行前会发射信号started(),操作系统将根据优先级参数调度线程。如果线程已经在运行,那么这个函数什么也不做。优先级参数的效果取决于操作系统的调度策略。特别是那些不支持线程优先级的系统优先级将会被忽略(例如在Linux中,更多细节请参考http://linux.die.net/man/2/sched_setscheduler)。

线程执行

int exec() [protected] 

进入事件循环并等待直到调用exit(),返回值是通过调用exit()来获得,如果调用成功则范围0。

void run() [virtual protected] 

线程的起点,在调用start()之后,新创建的线程就会调用这个函数,默认实现调用exec(),大多数需要重新实现这个函数,便于管理自己的线程。该方法返回时,该线程的执行将结束。

线程退出

void quit() [slot] 

告诉线程事件循环退出,返回0表示成功,相当于调用了QThread::exit(0)。

void exit(int returnCode = 0) 

告诉线程事件循环退出。 调用这个函数后,线程离开事件循环后返回,QEventLoop::exec()返回returnCode,按照惯例,0表示成功;任何非0值表示失败。

void terminate() [slot] 

终止线程,线程可能会立即被终止也可能不会,这取决于操作系统的调度策略,使用terminate()之后再使用QThread::wait(),以确保万无一失。当线程被终止后,所有等待中的线程将会被唤醒。 

警告:此函数比较危险,不鼓励使用。线程可以在代码执行的任何点被终止。线程可能在更新数据时被终止,从而没有机会来清理自己,解锁等等。。。总之,只有在绝对必要时使用此函数。

void requestInterruption() 

请求线程的中断。该请求是咨询意见并且取决于线程上运行的代码,来决定是否及如何执行这样的请求。此函数不停止线程上运行的任何事件循环,并且在任何情况下都不会终止它。

线程等待

void msleep(unsigned long msecs) [static]     //强制当前线程睡眠msecs毫秒void sleep(unsigned long secs) [static]     //强制当前线程睡眠secs秒void usleep(unsigned long usecs) [static]     //强制当前线程睡眠usecs微秒bool wait(unsigned long time = ULONG_MAX)     //线程将会被阻塞,等待time毫秒。和sleep不同的是,如果线程退出,wait会返回。

线程状态

bool isFinished() const     //线程是否结束bool isRunning() const     //线程是否正在运行bool isInterruptionRequested() const     //如果线程上的任务运行应该停止,返回true。可以使用requestInterruption()请求中断。 //此函数可用于使长时间运行的任务干净地中断。从不检查或作用于该函数返回值是安全的,但是建议在长时间运行的函数中经常这样做。注意:不要过于频繁调用,以保持较低的开销。

线程优先级

void setPriority(Priority priority) 

设置正在运行线程的优先级。如果线程没有运行,此函数不执行任何操作并立即返回。使用的start()来启动一个线程具有特定的优先级。优先级参数可以是QThread::Priority枚举除InheritPriortyd的任何值。

Qt多线程优先级
常量优先级
QThread::IdlePriority0没有其它线程运行时才调度
QThread::LowestPriority1比LowPriority调度频率低
QThread::LowPriority2比NormalPriority调度频率低
QThread::NormalPriority3操作系统的默认优先级
QThread::HighPriority4比NormalPriority调度频繁
QThread::HighestPriority5比HighPriority调度频繁
QThread::TimeCriticalPriority6尽可能频繁的调度
QThread::InheritPriority7使用和创建线程同样的优先级. 这是默认值

 

QThread类使用方式

QThread的使用方法有如下两种:

  • QObject::moveToThread()
  • 继承QThread类

QObject::moveToThread

方法描述:

  1. 定义一个继承于QObject的worker类,在worker类中定义一个槽slot函数doWork(),这个函数中定义线程需要做的工作;
  2. 在要使用线程的controller类中,新建一个QThread的对象和woker类对象,使用moveToThread()方法将worker对象的事件循环全部交由QThread对象处理;
  3. 建立相关的信号函数和槽函数进行连接,然后发出信号触发QThread的槽函数,使其执行工作。

例子:

#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include<QDebug>
#include<QThread>
class Worker:public QObject                    //work定义了线程要执行的工作
{Q_OBJECT
public:Worker(QObject* parent = nullptr){}
public slots:void doWork(int parameter)                        //doWork定义了线程要执行的操作{qDebug()<<"receive the execute signal---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId();for(int i = 0;i!=1000000;++i){++parameter;}qDebug()<<"      finish the work and sent the resultReady signal\n";emit resultReady(parameter);           //emit啥事也不干,是给程序员看的,表示发出信号发出信号}signals:void resultReady(const int result);               //线程完成工作时发送的信号
};#endif // WORKER_H
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>
class Controller : public QObject            //controller用于启动线程和处理线程执行结果
{Q_OBJECTQThread workerThread;
public:Controller(QObject *parent= nullptr);~Controller();
public slots:void handleResults(const int rslt)                        //处理线程执行的结果{qDebug()<<"receive the resultReady signal---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';qDebug()<<"     the last result is:"<<rslt;}
signals:void operate(const int);                        //发送信号触发线程
};#endif // CONTROLLER_H
#include "controller.h"
#include <worker.h>
Controller::Controller(QObject *parent) : QObject(parent)
{Worker *worker = new Worker;worker->moveToThread(&workerThread);            //调用moveToThread将该任务交给workThreadconnect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));            //operate信号发射后启动线程工作connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);            //该线程结束时销毁connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));            //线程结束后发送信号,对结果进行处理workerThread.start();                //启动线程qDebug()<<"emit the signal to execute!---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';emit operate(0);
}Controller::~Controller()        //析构函数中调用quit()函数结束线程
{workerThread.quit();workerThread.wait();
}

继承QThread类

方法描述

  • 自定义一个继承QThread的类MyThread,重载MyThread中的run()函数,在run()函数中写入需要执行的工作;
  • 调用start()函数来启动线程。

例子:

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include<QDebug>
class MyThread : public QThread
{Q_OBJECT
public:MyThread(QObject* parent = nullptr);
signals:                //自定义发送的信号void myThreadSignal(const int);
public slots:                //自定义槽void myThreadSlot(const int);
protected:void run() override;
};#endif // MYTHREAD_H
#include "mythread.h"MyThread::MyThread(QObject *parent)
{}void MyThread::run()
{qDebug()<<"myThread run() start to execute";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';int count = 0;for(int i = 0;i!=1000000;++i){++count;}emit myThreadSignal(count);exec();
}void MyThread::myThreadSlot(const int val)
{qDebug()<<"myThreadSlot() start to execute";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';int count = 888;for(int i = 0;i!=1000000;++i){++count;}
}
#include "controller.h"
#include <mythread.h>
Controller::Controller(QObject *parent) : QObject(parent)
{myThrd = new MyThread;connect(myThrd,&MyThread::myThreadSignal,this,&Controller::handleResults);connect(myThrd, &QThread::finished, this, &QObject::deleteLater);            //该线程结束时销毁connect(this,&Controller::operate,myThrd,&MyThread::myThreadSlot);myThrd->start();QThread::sleep(5);emit operate(999);
}Controller::~Controller()
{myThrd->quit();myThrd->wait();
}

两种方法的比较

两种方法来执行线程都可以,随便你的喜欢。不过看起来第二种更加简单,容易让人理解。不过我们的兴趣在于这两种使用方法到底有什么区别?其最大的区别在于:

  • moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。 
  • 子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行。如果你不明白的话,请看,第二个例子中,子类化的线程的槽函数中输出当前线程的ID,而这个ID居然是主线程的ID!!事实的确是如此,子类化的QThread只能执行run()函数中的任务直到run()函数退出,而它的槽函数根本不会被自己的线程执行。

QThread的信号与槽

启动或终止线程时,QThread提供了信号与槽。

QThread的信号
信号含义
void finished()终止线程实例运行,发送信号
void started()启动线程实例,发送信号
void terminated()结束线程实例,则发送信号
QThread的槽
含义
void quit()线程终止运行槽
void start(Priority)线程启动槽
void terminate()线程结束槽

 


http://chatgpt.dhexx.cn/article/AqbWQR1j.shtml

相关文章

Qt - 一文理解QThread多线程(万字剖析整理)

目录 为什么需要多线程QThread使用方法new QThread Class & Override run()new Object Class & moveToThread(new QThread) connect事件循环源码分析如何正确退出线程堆栈大小优先级线程间通讯线程同步互斥锁读写锁信号量条件变量 可重入与线程安全QObject的可重入性开…

Qt 线程中QThread的使用

文章目录 Qt 线程中QThread的使用1. 线程类 QThread1.1 常用共用成员函数1.2 信号槽1.3 静态函数1.4 任务处理函数 2. 使用方式 12.2 示例代码3. 使用方式 23.1 操作步骤3.2 示例代码 Qt 线程中QThread的使用 在进行桌面应用程序开发的时候&#xff0c; 假设应用程序在某些情况…

Qt之QThread(深入理解)

简述 为了让程序尽快响应用户操作&#xff0c;在开发应用程序时经常会使用到线程。对于耗时操作如果不使用线程&#xff0c;UI界面将会长时间处于停滞状态&#xff0c;这种情况是用户非常不愿意看到的&#xff0c;我们可以用线程来解决这个问题。 前面&#xff0c;已经介绍了…

Qt之QThread介绍(常用接口及实现、自动释放内存、关闭窗口时停止线程运行、同步互斥)

在程序设计中&#xff0c;为了不影响主程序的执行&#xff0c;常常把耗时操作放到一个单独的线程中执行。Qt对多线程操作有着完整的支持&#xff0c;Qt中通过继承QThread并重写run()方法的方式实现多线程代码的编写。针对线程之间的同步与互斥问题&#xff0c;Qt还提供了QMutex…

Qt线程QThread开启和安全退出

1、线程开启 Qt中&#xff0c;开启子线程&#xff0c;一般有两种方法&#xff1a; a, 定义工作类worker: worker继承 QThread, 重写run函数&#xff0c;在主线程中实例化worker&#xff0c;把耗时工作放进worker的run函数中完成&#xff0c;结束后&#xff0c;往主线程中发信…

QThread的用法

概述 QThread类提供了一个与平台无关的管理线程的方法。一个QThread对象管理一个线程。QThread的执行从run()函数的执行开始&#xff0c;在Qt自带的QThread类中&#xff0c;run()函数通过调用exec()函数来启动事件循环机制&#xff0c;并且在线程内部处理Qt的事件。在Qt中建立线…

Oracle 定时任务执行存储过程【建议收藏】

首先用一个完整的例子来实现定时执行存储过程。 任务目标&#xff1a;每小时向test表中插入一条数据。 实现方案&#xff1a; 1.通过 oracle 中 dbms_job 完成存储过程的定时调用 2.在存储过程中完成相应的逻辑操作 实现步骤&#xff1a; 1.创建一个测试表 create table test…

【Mysql】MySQL 用户执行存储过程的权限

问题 运行存储过程报错&#xff1a; 原因 查询资料&#xff1a; 1305错误&#xff0c;由于当前用户没用权限&#xff0c;对用户进行授权后可以执行。 解决 MySQL创建存储过程/函数需要的权限&#xff1a; alter routine---修改与删除存储过程/函数 create routine--创建…

goland 使用 gorm 执行 存储过程 : go语言 执行存储过程

使用 gorm 执行 存储过程 初安装依赖代码&#xff1a; 附存储过程图片存储过程代码&#xff08;创建&#xff09; 表结构表结构图表结构代码 初 最近遇到要写存储过程需求&#xff0c;使用 大佬写的 框架 gorm 来完成。简直是方便的不行&#xff1a; 直接上代码&#xff1a; …

JDBC之CallableStatement执行存储过程

​ 在前面的一篇文章中&#xff0c;我们学习使用Statement、PreparedStatement来完成对数据表的增删改查。而存储过程作为数据库的重要组成部分&#xff08;痛点&#xff0c;当时学的时候头发都掉了好几根&#x1f62d;&#xff09;&#xff0c;那JDBC是如何执行存储过程呢&…

mysql创建定时任务执行存储过程

存储过程已添加好&#xff1a;https://blog.csdn.net/YXWik/article/details/127283316 1.创建定时器用来执行存储过程函数 create event delete_data on schedule every 10 second do call delete_data();这里的第一行代表的创建名称为delete_data的事件 第二行是执行周期为…

Oracle 定时任务执行存储过程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、创建存储过程一、创建定时任务 一、创建存储过程 CREATE OR REPLACE PROCEDURE TESTCREATE AS --存储过程名称I INT : 1; --变量 BEGINWHILE I < 10 LOOP -…

oracle执行存储过程参数,Oracle 执行存储过程四种方法(带参数 不带参数)

1.如果是命令窗口就用exec 存储过程名&#xff1a; 1 EXEC procedure;--procedure是存储过程名 2.如果是 SQL窗口就用 begin 存储过程名 end; 1 2 3 begin procedure;--procedure是存储过程名 end; 3.如果是程序中调用就用 call 存储过程名 &#xff0c;举个栗子&#xff1…

plsql定时执行存储过程

1、创建定时向表中插入数据的存储过程&#xff0c;名为testJob。 CREATE OR REPLACE PROCEDURE "testJob" AS BEGIN EXECUTE IMMEDIATE INSERT INTO TABLE_HIS SELECT * FROM TABLE_AI; COMMIT; END; 2、使用plsql找到定时器对应的DBMS_Jobs文件夹&#xff0c;…

SQLserver存储过程简单写法与设置定时执行存储过程方法

最近工作中需要写SQLserver的存储过程&#xff0c;第一次使用&#xff0c;简单记录下&#xff0c;以防遗忘。 在SQLserver可视化工具中编写&#xff0c;我的工具如下图&#xff1a; 首先点击你的数据库&#xff0c;找到可编程性&#xff0c;在可编程性里面右击存储过程-->点…

symlink() 函数

查看更多 https://www.yuque.com/docs/share/10f959a4-bd7e-47a9-ad78-11a1310613f3

Install fail! Error: EPERM: operation not permitted, symlink

这个是在安装lodash的时候报错的情况 尝试了下方命令&#xff0c;但是还是有误 在这里可能是因为npm缓存的问题&#xff0c;我先删除了 C:\Users\abc(自己电脑的用户名) 文件夹下的 .npmrc 文件&#xff0c;但是没反应。然后运行了下方的命令&#xff1a; 运行&#xff1a;…

error: eperm: operation not permitted, symlink

查询当前配置的镜像 npm get registry > https://registry.npmjs.org/ 设置成淘宝镜像 npm config set registry http://registry.npm.taobao.org/

Linux Symbolic Links(软链接)

linux 中的链接有两种&#xff0c;分别是 Hard Links&#xff08;硬链接&#xff09;和Symbolic Links(软链接)。 1. 什么是硬链接、软链接&#xff1f; 硬链接&#xff1a;在另外一个位置创建源文件的链接文件&#xff0c;相当于复制了一份&#xff0c;占用资源会倍增。硬链…

linux link/symlink/unlink 硬连接和软连接介绍

文章目录 硬连接和软连接的区别硬连接软连接 链接命令lnln指令参数含义例子 硬链接函数link()符号链接函数symlink()解除链接函数unlink() link/symlink/unlink函数头文件为#include <unistd.h> 硬连接和软连接的区别 硬连接 硬连接指通过索引节点来进行连接。 在Lin…