Qt 线程

news2024/12/23 12:26:55

Qt中线程的解释: 

QThread(线程),代表一个在应用程序中可以独立控制的线程,可以和进程中的其他线程分享数据。QThread 对象管理程序中的一个控制线程。QThreads 在 run() 中开始执行。默认情况下,run() 通过调用 exec() 来启动事件循环,并在线程内运行 Qt 事件循环

线程的使用条件:常用于比较耗时的任务,使用线程执行的话,主界面才能正常运行。 

创建线程的方法:

  1. 继承QThread  重写run函数
  2. 继承QObject  通过moveToThread(thread),交给thread执行
  3. 继承QRunnable 重写run函数,但必须使用线程池(QThreadPool)运行

继承QThread的特点:

  1. 可以通过信号槽与外界进行通信
  2. 线程中的对象必须在run函数中创建
  3. 每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。 
  4. 要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。

(适用场景:那些常驻内存的任务)


继承QObject  通过moveToThread(thread)的特点:

  1. 使用比较简单方便
  2. 但只能执行槽函数中的内容,需要通过信号与槽的方式连接
  3. 创建对象时不能指定父类,如果指定父类,那么将无法调用到线程中

 注意:线程种无法使用任何界面部件类

 第一种创建方式:

class Thread : public QThread //继承QThread
{
    Q_OBJECT
public:
    explicit Thread(QObject *parent = nullptr);
protected:
    void run() override;//重写run函数
};

void Thread::run()
{
  //执行比较耗时的任务
}


//在主类中调用

Thread  thread1;

thread.start();//开起线程,就会执行run函数

第二种创建方式:

class Worker : public QObject //继承自QObject
{
    Q_OBJECT

public slots:
    void doWork(const QString &parameter) {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }

signals:
    void resultReady(const QString &result);
};

class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;//创建一个线程
public:
    Controller() {
        Worker *worker = new Worker;//创建一个工作对象
        worker->moveToThread(&workerThread);//移动到线程种执行
        //当线程执行完成后,销毁work对象
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();//开启线程
    }
    ~Controller() {
        workerThread.quit();//关闭线程
        workerThread.wait();//堵塞线程
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);

QThread 常用的函数:

exec()

进入事件循环并等待 exit() 被调用,返回传递给exit() 的值。如果通过 quit() 调用 exit(),则返回的值为 0。

此函数旨在从 run() 中调用。必须调用此函数才能启动事件处理

exit()告知线程的事件循环以返回代码退出
currentThread()返回指向管理当前正在执行的线程的 QThread 的指针。
idealThreadCount()返回可在系统上运行的理想线程数,如果无法检测到处理器内核数,则此函数返回 1。
isDone()如果线程完成返回true 否则返回 false
isRunning()如果线程正在运行返回true 否则返回 false
msleep()强制当前线程休眠毫秒
sleep()强制当前线程休眠几秒钟,此功能不保证准确性。在重负载条件下,应用程序可能会休眠超过
usleep()强制当前线程休眠 usecs微秒,此功能不保证准确性。在重负载条件下,应用程序可能比 usecs 休眠时间更长
requestInterruption()请求中断线程。该请求是建议性的,由线程上运行的代码决定是否以及如何对此类请求进行操作。此函数不会停止线程上运行的任何事件循环,也不会以任何方式终止它。
run()线程的起点,调用 start() 后,新创建的线程调用此函数,您可以重新实现此函数以促进高级线程管理
quit()告知线程的事件循环退出并返回代码 0(成功)。等效于调用 QThread::exit(0)
wait()阻塞线程,直到如果线程已完成,此函数将返回 true。如果线程尚未启动,它也返回 true。或到了最后期限。如果达到截止时间,此函数将返回 false
start()通过调用 run() 开始执行线程。操作系统将根据优先级参数调度线程。如果线程已在运行,则此函数不执行任何操作
terminate()终止线程的执行。线程可能会也可能不会立即终止,具体取决于操作系统的调度策略。在 terminate() 之后使用 QThread::wait() 可以肯定。

signals

finished()此信号在完成执行之前从关联的线程发出
started()在调用run() 函数之前,当关联线程开始执行时,此信号从关联线程发出

例子:

主界面有,开启,暂停,关闭按钮,在一个QLabel显示线程中的数据

创建一个myThread类继承自QThread

class myThread : public QThread
{
    Q_OBJECT
public:
    explicit myThread(QObject *parent = nullptr);

    void stop_thread()//暂停
    {
        thread_run=false;//设置为不可执行
    }

    void run()//重写run函数    (函数直接在这里实现,比较好查看)
    {
        while(thread_run)//如果可以执行
        {
            a+=10;
            emit show_number(a);//触发信号
            sleep(1);//休眠5秒
        }
        thread_run=true;

    }
protected:
    volatile bool thread_run=true;//判断是否可以运行
    int a=0;

signals:
    void show_number(int i);//发送信号给主窗口,显示数据

};

主类中的使用:

.cpp文件中:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    myThread *thread=new myThread;
    qDebug()<<"本机线程数"<<thread->idealThreadCount();//查看自己的线程数
    connect(ui->pushButton,&QPushButton::clicked,[=]()//开启线程
    {
        thread->start();
        qDebug()<<"线程已开启";
        qDebug()<<"线程id"<<thread->currentThreadId();
    });
    connect(ui->pushButton_2,&QPushButton::clicked,[=]()//暂停线程
    {
        thread->stop_thread();//暂停
        qDebug()<<"线程已暂停";
    });
    connect(ui->pushButton_3,&QPushButton::clicked,[=]()//结束线程
    {
        ui->lcdNumber->display(0);
        thread->stop_thread();//暂停
        thread->quit();
        thread->wait();
        thread->deleteLater();//销毁
        ui->pushButton->setEnabled(false);//开启不可点击
    });
    connect(thread,SIGNAL(show_number(int)),this,SLOT(show_data(int)));

}
void Widget::show_data(int s)
{
    ui->lcdNumber->display(s);
}

Widget::~Widget()
{
    delete ui;
}

重入和线程安全:

在整个文档中,重入和线程安全用于标记类和函数,从而表明怎样在多线程应用中使用它们。

  • 线程安全函数可以从多个线程同时调用,即使调用使用共享数据也是如此,因为对共享数据的所有引用都是序列化的。
  • 也可以从多个线程同时调用重函数,但前提是每个调用都使用自己的数据

以上可以得出结论:线程安全函数始终是可重入的,但可重入函数并不总是线程安全的。

通过扩展,如果可以从多个线程安全地调用一个类的成员函数,只要每个线程使用该类的不同实例,则该类就被称为可重入类。如果可以从多个线程安全地调用该类的成员函数,则该类是线程安全的,即使所有线程都使用该类的同一实例也是如此。

重入:

c++类通常是可重入的,因为它们只能访问自己的成员数据。任何线程都可以在可重入类的实例上调用成员函数,只要没有其他线程可以同时调用该类的同一实例上的成员函数。

例如:

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { ++n; }
    void decrement() { --n; }
    int value() const { return n; }

private:
    int n;
};

上面的类是可重入的,但这并不是线程安全的,当有多个线程同时修改类的一个成员变量,可能会

会产生多种结果 。因为++和--的操作并不是总是原子的(原子操作是指一个操作不会被其他线程中断)。它们会分为3个机械指令:

  • 在寄存器中加载变量的值
  • 递增或递减寄存器的值
  • 将寄存器的值存储回主存储器

当有多个线程同时加载变量的旧值,然后递增它们的寄存器,然后将其存储回去,最终它们会相互覆盖,变量只会递增一次。

线程安全:

显然,当有多个线程访问时,访问必须序列化,当有一个线程访问时,其他线程必须等待。

例如:使用QMutex保护数据的访问。

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { QMutexLocker locker(&mutex); ++n; }
    void decrement() { QMutexLocker locker(&mutex); --n; }
    int value() const { QMutexLocker locker(&mutex); return n; }

private:
    mutable QMutex mutex;
    int n;
};

在构造函数中自动锁定互斥锁,在函数结束时调用析构函数时解锁互斥锁,锁定互斥锁可确保序列化来自不同线程的访问。数据成员mutex是用限定符mutable声明的,因为我们需要锁定和解锁value中的互斥锁,因为这是一个 const 函数。

 同步线程:

线程的目的是允许并行运行,但有时线程必须停止等待其他线程。例如,如果两个线程尝试访问同一个变量,这样的话结果是未定义的。强制线程相互等待的原则成为互斥,是一种保护共享资源的常用技术。

同步线程类:

  • QMutex  互斥锁
  • QReadWriteLock  读-写锁
  • QSemaphore  信号量
  • QWaitCondition 条件变量

QMutex(互斥锁)

提供一个互斥锁,在任何事件至多有一个线程可以获得mutex。如果一个线程尝试获得mutex,而mutex已经锁住,那么这个线程将会睡眠

QReadWriteLock  (读-写锁)

读-写锁,于QMutex相似,但它对共享数据进行访问的区分,分为“读”,“写”访问,允许多个线程同时对数据进行“读”访问,QReadWiteLock的并行度大于QMutex

QSemaphore  信号量

QSemaphore是QMutex的概括,可保护一定数量的相同资源。相比之下,QMutex 只保护一种资源。信号量示例显示了信号量的典型应用:同步对生产者和使用者之间循环缓冲区的访问

QWaitCondition 条件变量

QWaitCondition,允许一个线程在一些条件满足时唤醒其他线程, 不是通过强制执行互斥而是通过提供条件变量来同步线程。一个或多个线程可以被堵塞来等待一个QWaitCondition,使用wakeOne()可以唤醒一个随机选取的等待的线程,使用wakeAll()可以唤醒所有正在等待的线程

 QMutex

QMutex通常和QMutexLocker一起使用,才可以确保这可以轻松确保锁定和解锁的执行一致。

常用函数:

isRecursive()判断互斥锁是否为递归(Qt 5.7引入)
lock()锁定互斥锁,如果另一个线程锁定了互斥锁,则此调用将阻塞,直到该线程解锁它
tryLock(int )尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false,可以设置等待时间。如果获得锁则必须使用unlock()解锁互斥锁,这样才能在另一个线程才能成功锁定它。
try_lock()尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false,提供此功能是为了与标准库概念兼容,等价于tryLock()(Qt  5.8 引入)
try_lock_for()

尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false,

如果获得锁,必须使用unlock()解锁互斥锁

如果为递归互斥锁,允许在同一线程的容易个互斥锁上多次调用该函数

如果此互斥锁是非递归互斥锁,则在尝试递归锁定互斥锁时,此函数将始终返回 false。(Qt  5.8 引入)

try_lock_until()

尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false,

如果获得锁,必须使用unlock()解锁互斥锁

如果为递归互斥锁,允许在同一线程的容易个互斥锁上多次调用该函数

如果此互斥锁是非递归互斥锁,则在尝试递归锁定互斥锁时,此函数将始终返回 false。(Qt  5.8 引入)

unlock()解锁互斥锁。

 QMutex::QMutex(QMutex::RecursionMode mode)

创建一个互斥锁时可以设置模式

QMutex::Recursive在这种模式下,线程可以多次锁定同一个互斥锁,并且在进行相应数量的 unlock() 调用之前,互斥锁不会被解锁
QMutex::NonRecursive在此模式下,线程只能锁定一次互斥锁

互斥锁使用场景:当一个变量同时被多个线程访问

int number=10;

void text()
{
    number*=2;
    number+=5;
}
void text1()
{
    number+=10;
    number*3;
}

正常调用 text()和text1()的话

text()  number=2*10=20+5=25
text1() number=25+10=30*3=90

同时调用的话可能会发生以下情况

线程1调用text()
number=10*2=20;
线程2调用text1(),现在text()的调用暂停
number=20+10=30
number=30*3=90;
继续完成线程text()
number=90+5=95

为了防止以上情况,可以上个互斥锁,使得调用完某个函数才能调用其他函数。

QMutex mutex;//互斥锁

int number=10;

void text()
{
     mutex.lock();//上锁
     number*=2;
     number+=5;
     mutex.unlock();//解锁  
}

void text1()
{
     mutex.lock();//上锁
     number+=10;
     number*=3;
     mutex.unlock();//解锁  
}

QMutexLocker类

QMutexLocker类是一个方便点的类,可以简化锁定和解锁的互斥锁。

使用方法:QMutexLocker应该在需要锁定QMutex的函数中创建,创建一个QMutexLock时,互斥锁被锁定。(更加方便)

函数:

mutex(()返回正在运行的互斥锁
relock()重新锁定未锁定的互斥锁锁。
unlock()解锁此互斥锁

 使用QMutex的情况:需要在分支中解锁互斥锁

int complexFunction(int flag)
{
    mutex.lock();

    int retVal = 0;

    switch (flag) {
    case 0:
    case 1:
        retVal = moreComplexFunction(flag);
        break;
    case 2:
        {
            int status = anotherFunction();
            if (status < 0) {
                mutex.unlock();
                return -2;
            }
            retVal = status + flag;
        }
        break;
    default:
        if (flag > 10) {
            mutex.unlock();
            return -1;
        }
        break;
    }

    mutex.unlock();
    return retVal;
}

QMutexLocker的使用

int complexFunction(int flag)
{
    QMutexLocker locker(&mutex);//创建一个QmutexLocker对象

    int retVal = 0;

    switch (flag) {
    case 0:
    case 1:
        return moreComplexFunction(flag);
    case 2:
        {
            int status = anotherFunction();
            if (status < 0)
                return -2;
            retVal = status + flag;
        }
        break;
    default:
        if (flag > 10)
            return -1;
        break;
    }

    return retVal;
}

当函数执行完后,QMutexLocker对象销毁时,互斥锁会解锁,就不用每个分支去解锁。

使用QMutexLocker::mutex()可以获取当前正在运行的互斥锁。 

 QReadWriteLock(读-写锁)

读写锁是一种同步工具,用于保护可以访问以进行读取和写入的资源。如果要允许多个线程同时具有只读访问权限,则这种类型的锁定很有用,但是一旦一个线程想要写入资源,就必须阻止所有其他线程,直到写入完成。

函数:

lockForRead()锁定读取,

如果另一个线程已锁定写入,此函数将阻止当前线程。

如果线程已锁定写入,则无法锁定读取。

lockForWrite()

锁定写入

如果另一个线程(包括当前线程)已锁定以进行读取或写入,则此函数将阻止当前线程

tryLockForRead()尝试锁定以进行读取,如果线程已锁定写入,则无法锁定读取
tryLockForWrite()尝试锁定以进行写入,如果线程已锁定以进行读取,则无法锁定写入
unlock()解锁

QReadWriteLock通常使用于经常读取数据,可以多个线程同时读取数据。

QReadWriteLock lock;

void ReaderThread::run()
{
    ...
    lock.lockForRead();
    read_file();
    lock.unlock();
    ...
}

void WriterThread::run()
{
    ...
    lock.lockForWrite();
    write_file();
    lock.unlock();
    ...
}

 QSemaphore(信号量)

 信号量是互斥锁的概括。虽然互斥锁只能锁定一次,但可以多次获取信号量。信号量通常用于保护一定数量的相同资源

函数:

acquire(int n)尝试获取n个由信号量保护的资源,当资源不够时将堵塞直到资源足够。
release(int n)

释放由信号量保护的 n 个资源,

此函数也可用于“创建”资源。

available()返回信号灯当前可用的资源数。这个数字永远不能是负数。
tryAcquire(int n)尝试获取由信号量保护的资源,成功返回true,否则返回false

tryAcquire(int n,int timeout

尝试获取由信号量保护的资源,成功返回true,否则返回false,调用最多等待timeout秒

 QSemaphore的创建

QSemaphore::QSemaphore(int n=0)

 创建新的信号量,并将其保护的资源数初始化为 n(默认为 0)。

    QSemaphore sem(5);
    sem.acquire(3);
    qDebug()<<"资源还有"<<sem.available()<<"个";
    sem.acquire(2);
    qDebug()<<"资源还有"<<sem.available()<<"个";
    sem.release(5);
    qDebug()<<"资源还有"<<sem.available()<<"个";

当释放的资源多余需要释放的资源时,多余的会进行创建

    QSemaphore sem(5);
    sem.acquire(3);
    qDebug()<<"资源还有"<<sem.available()<<"个";
    sem.release(5);
    qDebug()<<"资源还有"<<sem.available()<<"个";
    sem.release(10);
    qDebug()<<"资源还有"<<sem.available()<<"个";

 

当资源少于需要获取的资源时,不会获取成功。

    QSemaphore sem(5);
    if(sem.tryAcquire(7)){
        qDebug()<<"获取成功";
    }
    else
    {
        qDebug()<<"获取失败";
    }

 QSemaphore信号量的生产者消费者问题。

全局变量 :

const int DataSize=1000;//生产者的数据量
const int BufferSize=800;//缓冲区大小
char buffer[Buffersize];
QSemaphore freeBytes(BufferSize);//控制缓冲区的信号量
QSemaphore usedBytes;//控制已经使用的缓冲区

 生产者类:

class Producer :public QThread
{
public:
   void run();
}

void Producer::run()
{
    qsrand(QTime(0.0.0).secsTo(QTime::currentTime()));//随机数
    for(int i=0;i<DataSize;++i){
        freeBytes.acquire();
        buffer[%BufferSize]="ACGT"[(int)qrand %4];
        qDebug()<<QString("Producer:%1").arg(buffer[i%buffersize]);
        usedBytes.release();

}

消费者类:

class Consumer :public QThread
{
public:
   void run();
}

void Producer::run()
{
    for(int i=0;i<DataSize;++i){
        userBytes.acquire();
        qDebug()<<QString("Producer:%1").arg(buffer[i%buffersize]);
        freeBytes.release();
    }

}

 main()

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Producer producer;//生产者
    Consumer consumer;//消费者
    producer.start();//开启线程
    consumer.start();//开启线程
    //两个线程调用wait(),阻塞线程,确保两个线程在退出前都有时间能完成main()
    producer.wait();
    consumer.wait();
    return 0;
}

 QWaitCondition

允许一个线程在一些条件满足时唤醒其他线程, 不是通过强制执行互斥而是通过提供条件变量来同步线程。一个或多个线程可以被堵塞来等待一个QWaitCondition,使用wakeOne()可以唤醒一个随机选取的等待的线程,使用wakeAll()可以唤醒所有正在等待的线程

函数:

wait(QMutex*,time)释放锁定的互斥锁并等待等待条件
wakeOne()唤醒一个等待等待条件的线程。唤醒的线程取决于操作系统的调度策略,无法控制或预测。如果要唤醒特定线程,解决方案通常是使用不同的等待条件,并让不同的线程等待不同的条件
wakeAll()唤醒等待等待条件的所有线程。线程的唤醒顺序取决于操作系统的调度策略,无法控制或预测。
notify_one()

提供此函数是为了与 STL 兼容。它等效于 wakeOne()Qt 5.8中引入

notify_all()提供此函数是为了与 STL 兼容。它等效于 wakeAll()Qt 5.8中引入

 QWaitCondition的示例:

使用QWaitCondition的QMutex解决生产者-消费者问题

设置全局变量: 

const int DataSize = 100000;//生产者将生成的数据量

const int BufferSize = 8192;//缓冲区
char buffer[BufferSize];

//两个等待条件,一个互斥锁和计数器
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;

生产者类:

class Producer : public QThread
{
public:
    Producer(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();//上锁
            if (numUsedBytes == BufferSize)//检查缓冲区是否已满
                bufferNotFull.wait(&mutex);//已满的话等待条件满足
            mutex.unlock();//解锁
            //存放数据(随机数)
            buffer[i % BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];
            
            mutex.lock();//上锁
            ++numUsedBytes;计数器+1
            bufferNotEmpty.wakeAll();//唤醒全部线程
            mutex.unlock();//解锁
        }
    }
};

消费者类:

class Consumer : public QThread
{
    Q_OBJECT
public:
    Consumer(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            if (numUsedBytes == 0)//检查缓冲区是否为空
                bufferNotEmpty.wait(&mutex);
            mutex.unlock();

            fprintf(stderr, "%c", buffer[i % BufferSize]);//输出内容

            mutex.lock();
            --numUsedBytes;//计数器-1
            bufferNotFull.wakeAll();
            mutex.unlock();
        }
        fprintf(stderr, "\n");
    }

signals:
    void stringConsumed(const QString &text);
};

main函数:

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Producer producer;//生产者
    Consumer consumer;//消费者
    producer.start();//开启线程
    consumer.start();//开启线程
    //两个线程调用wait(),阻塞线程,确保两个线程在退出前都有时间能完成main()
    producer.wait();
    consumer.wait();
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358361.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DDFN: Decoupled Dynamic Filter Networks解耦的动态卷积

一、论文信息 论文名称&#xff1a;Decoupled Dynamic Filter Networks 论文&#xff1a;https://thefoxofsky.github.io/files/ddf.pdf 代码&#xff1a;https://github.com/theFoxofSky/ddfnet 主页&#xff1a;https://thefoxofsky.github.io/project_pages/ddf 作者团…

kubectl常用的命令

目录 安装 kubectl 一、命令自动补全 二、常用命令 1、查看所有pod列表 2、查看RC和service列表 3、显示Node的详细信息 4、显示Pod的详细信息, 特别是查看Pod无法创建的时候的日志 5、 根据yaml创建资源, apply可以重复执行&#xff0c;create不行 6、基于nginx.yaml…

实验室设计SICOLAB第三方检测中心实验室设计

第三方检测中心实验室怎么设计&#xff1f;详细设计内容有哪些&#xff1f;功能区域有哪些&#xff1f;仪器有哪些&#xff1f;要多少面积&#xff1f;第三方检测中心实验室是一种独立的实验室&#xff0c;为客户提供各种测试和分析服务。以下是一个第三方检测中心实验室的详细…

给你安利几款好用的谷歌浏览器插件

给你安利几款好用的谷歌浏览器插件前言一 Octotree 插件二 GitCodeTree 插件三 SourceGraph 插件四 GitZip 插件五 Enhanced GitHub 插件六 插件下载安装6.1 谷歌应用商店下载6.2 离线安装6.2.1 下载插件6.2.2 安装插件七 移除、启用、停用插件小结前言 GitHub是全球最大的代码…

Windows PowerShell中成功进入conda虚拟环境

本人操作系统是Windows10&#xff08;输入命令cmd或在运运行中输入winver查看&#xff09;在cmd命令行中大家都很熟悉&#xff0c;很方便进入到指定创建了的虚拟环境中&#xff0c;那么在PowerShell中怎么进入呢&#xff1f;比如在VSCode中的TERMINAL使用的是PowerShell&#x…

论文阅读笔记-DiffusionInst: Diffusion Model for Instance Segmentation

文章目录DiffusionInst: Diffusion Model for Instance Segmentation摘要介绍任务介绍实例分割的几种方法想法来源贡献方法整体结构Mask RepresentationDiffusionInst组成TrainingInference不足之处感悟DiffusionInst: Diffusion Model for Instance Segmentation 代码&#x…

魔兽世界经典怀旧服务器架设教程

准备工具&#xff1a;MySQL服务端服务器最重要的你需要会技术、要不然都瞎扯 给你东西你也看不懂。教程开始&#xff1a;安装MySQL并创建数据库安装MySQL社区版&#xff0c;并配置SQL服务器。安装SQLyog。利用其登录&#xff0c;创建realmd、characters、mangos、scriptdev2数据…

普通动物实验室规划设计SICOLAB

一、普通动物实验室普通动物实验室是进行动物实验的专门场所&#xff0c;用于研究疾病的发生机制&#xff0c;测试药物的安全性和有效性等。以下是普通动物实验室设计的一些细节和功能房间&#xff1a;&#xff08;1&#xff09;动物饲养区&#xff1a;用于饲养动物&#xff0c…

面试浅谈之 C++ STL 篇

面试浅谈之 C STL 篇 一 &#x1f3e0; 概述 HELLO&#xff0c;各位博友好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 这里是面试浅谈系列&#xff0c;收录在专栏面试中 &#x1f61c;&#x1f61c;&#x1f61c; 本系列将记录一些阿呆个人整理的面试题…

hydra常见端口服务穷举

目录 工具介绍 参数说明 官方示例 官方字典 ssh爆破 ftp爆破 mysql爆破 smb爆破 rdb爆破 http爆破 redis爆破 工具介绍 hydra 是一个支持众多协议的爆破工具&#xff0c;已经集成到KaliLinux中&#xff0c;直接在终端打开即可 参数说明 -l &#xff1a; 指定破…

Linux下安装MySQL8.0的详细步骤(解压tar.xz安装包方式安装)

Linux下安装MySQL8.0的详细步骤 第一步&#xff1a;下载安装配置 第二步&#xff1a;修改密码&#xff0c;并设置远程连接&#xff08;为了可以在别的机器下面连接该mysql&#xff09; 第三步&#xff1a;使用Navicat客户端连接 搞了一台云服务器&#xff0c;首先要干的活就是…

move_base代码解析(二)MoveBase::planThread

在第一章中第四步执行了planThread函数的线程启动&#xff0c;该步骤会调用planThread函数。这里不是通过函数调用的形式实现的&#xff0c;而是通过线程开关的形式实现的: boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_);//给该线程上锁planner_goal…

117页数字化转型与产业互联网发展趋势及机会分析报告(PPT)

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 产业互联网是以机构组织为主体的…

CentOS7 Hive2.3.9 安装部署(mysql 8.0)

一、CentOS7安装MySQL数据库 查询载mariadb rpm -qa | grep mariadb卸载mariadb rpm -e --nodeps [查询出来的内容]安装wget为下载mysql准备 yum -y install wget在tools目录下执行以下命令&#xff0c;下载MySQL的repo源&#xff1a; wget -P /tools/ https://dev.mysql.…

网络编程学习一

1、初识网络编程2、网络编程三要素3、三要素&#xff08;IP&#xff09;4、IPV4的一些小细节5、Inetaddress类的使用package com.leitao.demo.network;import java.net.InetAddress; import java.net.UnknownHostException;/*** Description: TODO* Author LeiTao* Date 2023/2…

移动WEB开发三、flex布局

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

企业数字化管理是什么?如何建立企业数字化管理?

企业数字化管理是什么&#xff0c;如何建立企业数字化管理&#xff1f; 01 什么是企业数字化管理&#xff1f; 提到企业数字化管理&#xff0c;就不得不联想到机器自动化、业务流程自动化以及数字处理自动化这三个概念的区别&#xff1a; ​而抛开那些又臭又长的概念解读&…

支持域自适应的可解释网络改进药物-靶标预测

预测药物-靶点相互作用是药物发现的关键。最近基于深度学习的方法表现出良好的性能&#xff0c;但仍然存在两个挑战&#xff1a; 如何明确地建模和学习药物和靶标之间的局部相互作用&#xff0c;以更好地预测和解释&#xff1b;如何优化新型药物-靶标对预测的泛化性能。 在这…

高斯课堂 计算机网络(下)

第四章、网络层 0、第四章导图 1、网络层概述及IP地址&#xff08;上&#xff09; &#xff08;1&#xff09;网络层概述 无连接指的是在网络层这一层次&#xff0c;我们在发送数据的时候&#xff0c;不需要实现建立连接&#xff0c;这种建立连接的可靠性由谁来保证呢&#x…

【Python入门第十三天】Python 元组

元组&#xff08;Tuple&#xff09; 元组是有序且不可更改的集合。在 Python 中&#xff0c;元组是用圆括号编写的。 实例 创建元组&#xff1a; thistuple ("apple", "banana", "cherry") print(thistuple)运行实例 访问元组项目 您可以通…