【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析(二)

简介: 【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析

【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析(一)https://developer.aliyun.com/article/1464088


四、Qt线程安全与同步机制(Thread Safety and Synchronization in Qt)

在多线程编程中,线程安全和同步是非常重要的概念。当多个线程同时访问共享资源时,可能会引发竞争条件(race conditions),导致程序行为不确定或出现错误。为了确保线程安全,需要使用同步机制来保护共享资源。

Qt提供了多种线程安全与同步机制,可以在多线程编程中使用。

4.1 QMutex(互斥锁)

互斥锁(mutex)是一种同步原语,用于保护对共享资源的访问。QMutex类提供了一个互斥锁,可以在Qt应用程序中使用。

以下是使用QMutex保护共享资源的一个示例:

#include <QMutex>
class SharedData
{
public:
    void setData(int data)
    {
        m_mutex.lock(); // 加锁
        m_data = data;
        m_mutex.unlock(); // 解锁
    }
    int getData()
    {
        int result;
        m_mutex.lock(); // 加锁
        result = m_data;
        m_mutex.unlock(); // 解锁
        return result;
    }
private:
    QMutex m_mutex; // 互斥锁
    int m_data; // 共享数据
};

在这个示例中,SharedData类包含一个共享的整数数据和一个互斥锁。setData()getData()函数在访问共享数据时使用互斥锁来保护共享资源。

在实际应用中,可以使用QMutex的lock()unlock()函数保护对共享资源的访问。当一个线程锁定互斥锁时,其他试图锁定该互斥锁的线程将被阻塞,直到互斥锁被解锁。这可以防止多个线程同时访问共享资源,从而确保线程安全。

4.2 QMutexLocker(互斥锁管理器)

为了简化互斥锁的使用和防止死锁,Qt提供了QMutexLocker类,它是一个方便的RAII(Resource Acquisition Is Initialization)风格的互斥锁管理器。当创建一个QMutexLocker对象时,它会自动锁定给定的互斥锁。当QMutexLocker对象超出其作用域或被删除时,它会自动解锁互斥锁。

以下是使用QMutexLocker保护共享资源的一个示例:

#include <QMutex>
#include <QMutexLocker>
class SharedData
{
public:
    void setData(int data)
    {
        QMutexLocker locker(&m_mutex); // 加锁
        m_data = data;
        // locker对象超出作用域时自动解锁
    }
    int getData()
    {
        int result;
        QMutexLocker locker(&m_mutex); // 加锁
        result = m_data;
        // locker对象超出作用域时自动解锁
        return result;
    }
private:
    QMutex m_mutex; // 互斥锁
    int m_data; // 共享数据
};

在这个示例中,SharedData类的实现与前一个示例相似,但使用了QMutexLocker来简化互斥锁的使用。QMutexLocker可以确保在函数返回或异常抛出时互斥锁始终被解锁,从而避免死锁。

4.3 QSemaphore(信号量)

信号量是另一种同步原语,用于控制对共享资源的并发访问。QSemaphore类提供了一个信号量,可以在Qt应用程序中使用。信号量管理一个资源计数器,当线程请求资源时,计数器减1;当线程释放资源时,计数器加1。如果计数器为0,请求资源的线程将阻塞,直到有其他线程释放资源。

以下是使用QSemaphore保护有限数量的共享资源的一个示例:

#include <QSemaphore>
class SharedResources
{
public:
    SharedResources(int resourcesCount)
        : m_semaphore(resourcesCount) // 初始化信号量
    {
    }
    void acquireResource()
    {
        m_semaphore.acquire(); // 请求资源
    }
    void releaseResource()
    {
        m_semaphore.release(); // 释放资源
    }
private:
    QSemaphore m_semaphore; // 信号量
};

在这个示例中,SharedResources类包含一个信号量,用于保护有限数量的共享资源。线程可以通过调用acquireResource()releaseResource()函数来请求和释放资源。如果所有资源都被占用,请求资源的线程将阻塞,直到有其他线程释放资源。

4.4 QReadWriteLock(读写锁)

读写锁是一种特殊类型的锁,允许多个线程同时读共享资源,但只允许一个线程在任何时候写共享资源。QReadWriteLock类提供了一个读写锁,可以在Qt应用程序中使用。读写锁的使用可以提高程序的性能,特别是在读操作远多于写操作的情况下。

以下是使用QReadWriteLock保护共享资源的一个示例:

#include <QReadWriteLock>
class SharedData
{
public:
    void setData(int data)
    {
        m_lock.lockForWrite(); // 获取写锁
        m_data = data;
        m_lock.unlock(); // 释放锁
    }
    int getData()
    {
        int result;
        m_lock.lockForRead(); // 获取读锁
        result = m_data;
        m_lock.unlock(); // 释放锁
        return result;
    }
private:
    QReadWriteLock m_lock; // 读写锁
    int m_data; // 共享数据
};

在这个示例中,SharedData类包含一个共享的整数数据和一个读写锁。setData()函数在访问共享数据时获取写锁,而getData()函数在访问共享数据时获取读锁。这可以确保在写入数据时不会有其他线程读取或写入数据,同时允许多个线程同时读取数据。

与QMutex类似,Qt还提供了一个RAII风格的读写锁管理器QReadLocker和QWriteLocker,用于简化读写锁的使用。当创建一个QReadLocker或QWriteLocker对象时,它会自动锁定给定的读写锁以进行读或写操作。当QReadLocker或QWriteLocker对象超出其作用域或被删除时,它会自动解锁读写锁。

4.5 QAtomic 类

QAtomic 类是 Qt 提供的用于原子操作的线程安全类。原子操作是一种不需要加锁的操作,因此可以提高性能。QAtomic 类包括:

  • QAtomicInt:用于原子整数操作的类。
  • QAtomicPointer:用于原子指针操作的类。

以下是使用 QAtomicInt 的一个示例:

#include <QAtomicInt>
class Counter
{
public:
    void increment()
    {
        m_count.ref(); // 原子地递增计数器
    }
    void decrement()
    {
        m_count.deref(); // 原子地递减计数器
    }
    int getCount() const
    {
        return m_count.load(); // 原子地加载计数器值
    }
private:
    QAtomicInt m_count; // 原子整数
};

在这个示例中,Counter 类包含一个原子整数 m_countincrement()decrement() 函数分别使用 ref()deref() 方法原子地递增和递减计数器。getCount() 函数使用 load() 方法原子地加载计数器的值。

使用 QAtomic 类可以避免互斥锁带来的性能开销,但请注意,原子操作并不能解决所有线程安全问题。在使用 QAtomic 类时,仍然需要关注线程安全和同步问题,确保程序正确运行。

4.6 QThreadStorage 类

QThreadStorage 类是 Qt 提供的线程局部存储(Thread Local Storage, TLS)工具。线程局部存储是一种特殊的存储机制,每个线程拥有自己的存储空间,线程之间的数据是隔离的。使用 QThreadStorage 可以避免共享资源的竞争问题,提高线程安全性和性能。

以下是使用 QThreadStorage 的一个示例:

#include <QThread>
#include <QThreadStorage>
class MyThread : public QThread
{
public:
    void run() override
    {
        if (!m_localData.hasLocalData()) // 检查当前线程是否有局部数据
        {
            m_localData.setLocalData(new int(0)); // 为当前线程分配局部数据
        }
        int *localValue = m_localData.localData(); // 获取当前线程的局部数据
        for (int i = 0; i < 10; ++i)
        {
            ++(*localValue);
            qDebug() << "Thread" << currentThreadId() << "local value:" << *localValue;
        }
        delete localValue; // 清理局部数据
        m_localData.setLocalData(nullptr);
    }
private:
    static QThreadStorage<int *> m_localData; // 线程局部存储
};
QThreadStorage<int *> MyThread::m_localData; // 初始化静态成员变量

在这个示例中,MyThread 类继承自 QThread 类。每个 MyThread 实例代表一个独立的线程。run() 函数是线程的主要执行方法。在 run() 函数中,我们使用 QThreadStorage 类的 hasLocalData()setLocalData()localData() 方法来管理线程的局部数据。这些方法操作的数据仅在当前线程内可见,线程之间的数据是隔离的。

使用 QThreadStorage 可以简化多线程编程,提高线程安全性和性能。但请注意,线程局部存储不适用于所有场景,特别是当需要在线程之间共享数据时。在使用 QThreadStorage 时,请确保线程安全和同步问题得到妥善处理。

五、Qt线程编程方法三:使用QtConcurrent框架(Using QtConcurrent Framework)

QtConcurrent框架为并发编程提供了一个高级接口,它允许您简化多线程应用程序的开发过程,特别是在执行一些可以并行化的任务时。QtConcurrent框架自动管理线程创建、分配和回收,让您专注于任务逻辑。

5.1 QtConcurrent框架简介(Introduction to QtConcurrent Framework)

QtConcurrent框架与QFuture和QFutureWatcher类配合使用,使您能够在主线程中轻松监视任务的进度和结果。

QtConcurrent框架是Qt库的一部分,为并发编程提供了一个高级抽象。它允许您简化多线程应用程序的开发过程,特别是在执行一些可以并行化的任务时。QtConcurrent框架自动管理线程创建、分配和回收,让您专注于任务逻辑。以下是QtConcurrent框架的主要特点:

  1. 简化线程管理:QtConcurrent框架通过自动管理线程池来简化线程管理。您无需手动创建和销毁线程,而只需关注任务的逻辑。这减轻了开发人员的负担,降低了出错的可能性。
  2. 函数式编程风格:QtConcurrent框架采用函数式编程风格,使得代码更简洁、易于理解。您可以将任务表示为纯函数,然后将其传递给QtConcurrent来执行。
  3. 支持容器操作:QtConcurrent框架提供了一系列并行容器操作,如map、mapped和filtered等。这些操作可以在多个线程上同时执行,以提高处理速度。这些操作也支持自定义的谓词和转换函数,使得代码具有更好的灵活性。
  4. 与QFuture和QFutureWatcher配合使用:QtConcurrent框架与QFuture和QFutureWatcher类配合使用,使您能够在主线程中轻松监视任务的进度和结果。QFuture封装了任务的结果,而QFutureWatcher用于监视任务的状态变化,并发出相应的信号。
  5. 可扩展性:QtConcurrent框架的设计具有很好的可扩展性。随着硬件资源的增加,例如更多的处理器核心,QtConcurrent框架可以自动利用这些资源来提高程序的性能。

QtConcurrent框架主要包括以下几个部分:

  • QtConcurrent::run:用于启动一个可以在后台线程中运行的函数。
  • QtConcurrent::map:用于对一个容器中的所有元素应用一个函数。
  • QtConcurrent::mapped:用于创建一个新的容器,其中包含将一个函数应用于原始容器中的所有元素所得到的结果。
  • QtConcurrent::filtered:用于创建一个新的容器,其中包含满足给定谓词的原始容器中的元素。

5.2 使用QtConcurrent实现并行任务(Implementing Parallel Tasks with QtConcurrent)

在这一节中,我们将介绍如何使用QtConcurrent实现并行任务。

5.2.1 使用QtConcurrent::run启动后台任务

QtConcurrent::run用于启动一个可以在后台线程中运行的函数。以下是一个简单的使用QtConcurrent::run启动后台任务的示例:

#include <QCoreApplication>
#include <QDebug>
#include <QtConcurrent/QtConcurrent>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 定义一个简单的任务
    auto task = []() {
        qDebug() << "Task started in thread" << QThread::currentThread();
        QThread::sleep(3);
        qDebug() << "Task finished in thread" << QThread::currentThread();
    };
    // 使用QtConcurrent::run启动任务
    QFuture<void> future = QtConcurrent::run(task);
    qDebug() << "Task started in main thread" << QThread::currentThread();
    // 等待任务完成
    future.waitForFinished();
    qDebug() << "Task finished in main thread" << QThread::currentThread();
    return a.exec();
}

在这个示例中,我们定义了一个简单的任务,然后使用QtConcurrent::run在后台线程中启动它。QFuture对象用于表示任务的结果,可以用来检查任务是否完成,或等待任务完成。

5.2.2 使用QtConcurrent::map和QtConcurrent::mapped对容器中的元素应用函数

QtConcurrent::map用于对一个容器中的所有元素应用一个函数。这个操作将在多个线程中并行执行,以提高处理速度。以下是一个使用QtConcurrent::map的示例:

#include <QCoreApplication>
#include <QDebug>
#include <QVector>
#include <QtConcurrent/QtConcurrent>
void multiplyByTwo(int &value)
{
    value *= 2;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVector<int> vector = {1, 2, 3, 4, 5};
    // 使用QtConcurrent::map对容器中的所有元素应用函数multiplyByTwo
    QFuture<void> future = QtConcurrent::map(vector, multiplyByTwo);
    // 等待任务完成
    future.waitForFinished();
    qDebug() << "Result:" << vector;
    return a.exec();
}

QtConcurrent::mapped用于创建一个新的容器,其中包含将一个函数应用于原始容器中的所有元素所得到的结果。以下是一个使用QtConcurrent::mapped的示例:

#include <QCoreApplication>
#include <QDebug>
#include <QVector>
#include <QtConcurrent/QtConcurrent>
int multiplyByTwo(int value)
{
    return value * 2;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVector<int> vector = {1, 2, 3, 4, 5};
    
      // 使用QtConcurrent::mapped创建一个新的容器,其中包含将函数multiplyByTwo应用于原始容器中的所有元素所得到的结果
       QFuture<QVector<int>> future = QtConcurrent::mapped(vector, multiplyByTwo);
// 等待任务完成
future.waitForFinished();
QVector<int> result = future.result();
qDebug() << "Result:" << result;
return a.exec();

在这个示例中,我们使用QtConcurrent::mapped将multiplyByTwo函数应用于原始容器中的所有元素,然后创建了一个新的容器来存储结果。QFuture对象用于表示任务的结果,可以用来检查任务是否完成,或等待任务完成。

5.3 QtConcurrent实例与分析(Examples and Analysis of QtConcurrent)

以下是使用QtConcurrent::map对一个容器中的所有元素应用一个函数的示例:

#include <QCoreApplication>
#include <QDebug>
#include <QVector>
#include <QtConcurrent/QtConcurrent>
void multiplyByTwo(int &value)
{
    value *= 2;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVector<int> vector = {1, 2, 3, 4, 5};
  // 使用QtConcurrent::map对容器中的所有元素应用函数multiplyByTwo
  QFuture<void> future = QtConcurrent::map(vector, multiplyByTwo);
  // 等待任务完成
  future.waitForFinished();
  qDebug() << "Result:" << vector;
  return a.exec();
}

在这个示例中,我们使用QtConcurrent::map将multiplyByTwo函数应用于一个整数向量中的所有元素。这个操作将在多个线程中并行执行,以提高处理速度。当任务完成后,我们输出结果向量。

5.3.1 实例:使用QtConcurrent计算文件的MD5值

在这个示例中,我们将使用QtConcurrent计算多个文件的MD5值。我们将使用QtConcurrent::mapped和QtConcurrent::run来完成这个任务。

#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QCryptographicHash>
#include <QFile>
#include <QtConcurrent/QtConcurrent>
// 定义一个函数来计算文件的MD5值
QByteArray fileMd5(const QString &filePath)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly))
    {
        qWarning() << "Failed to open file" << filePath;
        return QByteArray();
    }
    QCryptographicHash hash(QCryptographicHash::Md5);
    if (hash.addData(&file))
    {
        return hash.result().toHex();
    }
    else
    {
        qWarning() << "Failed to compute MD5 for file" << filePath;
        return QByteArray();
    }
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 获取要计算MD5值的文件列表
    QStringList fileList = QDir("/path/to/your/files").entryList(QDir::Files);
    // 使用QtConcurrent::mapped并行计算文件的MD5值
    QFuture<QByteArray> future = QtConcurrent::mapped(fileList, fileMd5);
    // 等待任务完成
    future.waitForFinished();
    // 输出计算结果
    for (int i = 0; i < fileList.size(); ++i)
    {
        qDebug() << "File:" << fileList[i] << "MD5:" << future.resultAt(i);
    }
    return a.exec();
}

在这个示例中,我们首先获取要计算MD5值的文件列表,然后使用QtConcurrent::mapped并行计算文件的MD5值。最后,我们输出计算结果。

5.3.2 分析

通过使用QtConcurrent框架,我们可以简化并行任务的实现。在这个示例中,我们无需手动管理线程池,而只需关注任务本身。QtConcurrent自动管理线程创建、分配和回收,减轻了开发人员的负担。

此外,QtConcurrent::mapped函数可以将任务拆分为多个子任务,并在多个线程上并行执行。这样可以充分利用多核处理器的性能,提高计算速度。

然而,QtConcurrent框架并不适用于所有场景。在某些情况下,如任务之间有复杂的依赖关系,或需要精确控制线程的执行顺序时,使用QThread类和信号槽机制可能更合适。

在实际开发中,您需要根据具体需求选择合适的线程编程方法。在适用的场景下,QtConcurrent框架可以大大简化多

六、Qt线程编程方法四:使用 QThreadPool 和 QRunnable

QThreadPool 是 Qt 提供的一个线程池类,可以用来管理和回收线程资源。与创建和销毁 QThread 实例相比,使用 QThreadPool 可以减少线程创建和销毁的开销,提高程序性能。

QRunnable 是一个抽象类,用于封装可以在线程池中执行的任务。通过继承 QRunnable 并实现其 run() 函数,您可以定义自己的任务类。

6.1 QThreadPool 简介(Introduction to QThreadPool)

QThreadPool 类可以创建和管理一组线程,以并发执行任务。线程池可以自动管理线程的创建和销毁,以及任务的分配。当任务完成时,线程会返回到线程池,等待下一个任务。这可以减少线程创建和销毁的开销,提高程序性能。

QThreadPool 提供了一些实用的功能,如设置线程池的最大线程数、等待所有任务完成以及取消所有未执行的任务等。

QThreadPool 类是一个线程池类,用于管理并发执行的任务。线程池在内部维护一组工作线程,这些线程可重复利用以减少线程创建和销毁的开销。QThreadPool 提供以下功能:

  • 自动创建和销毁线程:线程池会根据任务的数量和负载自动创建新的线程,当线程空闲一段时间后,线程池会自动销毁线程,释放资源。
  • 限制最大线程数:线程池可以设置最大线程数,以防止线程数量过多导致系统资源耗尽。当线程池中的线程达到最大值时,新提交的任务将等待,直到有空闲线程可用。
  • 线程优先级:QThreadPool 允许为任务设置优先级,优先级较高的任务会优先分配给空闲线程执行。
  • 任务排队策略:线程池可以设置任务排队策略,例如先进先出(FIFO)或后进先出(LIFO)等。这可以根据任务特性和应用需求进行调整,以实现更好的性能。
  • 全局线程池实例:QThreadPool 提供一个全局线程池实例,可以通过 QThreadPool::globalInstance() 函数获取。全局线程池实例适用于大多数场景,简化了线程池的使用。

通过使用 QThreadPool,您可以更有效地管理线程资源,提高多线程程序的性能。同时,它还简化了多线程编程,让您能够专注于任务逻辑,而无需关注线程的创建、销毁和调度。

6.2 创建自定义 QRunnable(Creating Custom QRunnable)

要在线程池中执行任务,需要创建一个继承自 QRunnable 的自定义类,并重写其 run() 函数。以下是一个简单的自定义 QRunnable 类示例:

#include <QRunnable>
#include <QDebug>
#include <QThread>
class MyRunnable : public QRunnable
{
public:
    void run() override
    {
        qDebug() << "Running task in thread" << QThread::currentThread();
        // 在这里执行任务逻辑
    }
};

在这个示例中,我们创建了一个名为 MyRunnable 的自定义任务类,并在 run() 函数中输出了当前线程信息。您可以在 run() 函数中添加您需要执行的任务逻辑。

6.2.1 传递参数给 QRunnable

要将参数传递给自定义 QRunnable 类,您可以在类中添加成员变量和构造函数。以下是一个传递参数给 QRunnable 的示例:

#include <QRunnable>
#include <QDebug>
#include <QThread>
class MyRunnable : public QRunnable
{
public:
    explicit MyRunnable(int value)
        : m_value(value)
    {
    }
    void run() override
    {
        qDebug() << "Running task with value" << m_value << "in thread" << QThread::currentThread();
        // 在这里执行任务逻辑
    }
private:
    int m_value;
};

在这个示例中,我们在 MyRunnable 类中添加了一个整数成员变量 m_value,并通过构造函数将其初始化。这样,我们就可以在创建 MyRunnable 实例时传递参数。

6.2.2 使用信号与槽

尽管 QRunnable 不是 QObject 的子类,但您仍然可以在自定义 QRunnable 类中使用信号与槽。您可以将 QRunnable 类中的信号与槽与其他 QObject 子类的对象连接,以实现线程间的通信。

要在 QRunnable 类中使用信号与槽,您需要将 QRunnable 与 QObject 组合,而不是将 QRunnable 作为 QObject 的子类。以下是一个在 QRunnable 类中使用信号与槽的示例:

#include <QObject>
#include <QRunnable>
#include <QDebug>
#include <QThread>
class MyWorker : public QObject
{
    Q_OBJECT
public:
    void process()
    {
        qDebug() << "Processing in thread" << QThread::currentThread();
        emit finished();
    }
signals:
    void finished();
};
class MyRunnable : public QRunnable
{
public:
    MyRunnable(MyWorker *worker)
        : m_worker(worker)
    {
    }
    void run() override
    {
        connect(this, &MyRunnable::finished, m_worker, &MyWorker::deleteLater, Qt::DirectConnection);
        m_worker->process();
        emit finished();
    }
signals:
    void finished();
private:
    MyWorker *m_worker;
};

在这个示例中,我们创建了一个名为 MyWorker 的 QObject 子类,并在其中定义了一个名为 process() 的槽。然后,我们在 MyRunnable 类中添加了一个 MyWorker 指针,并在 run() 函数中连接信号与槽。这样,我们就可以在不同线程中使用信号与槽进行通信。

6.3 使用 QThreadPool 管理线程(Managing Threads with QThreadPool)

要使用 QThreadPool 管理线程并执行任务,可以按照以下步骤操作:

  1. 创建一个 QThreadPool 实例。通常情况下,可以使用 QThreadPool 的全局实例,通过 QThreadPool::globalInstance() 函数获取。
  2. 创建一个自定义 QRunnable 实例。
  3. 将 QRunnable 实例提交给线程池,线程池会自动选择一个空闲线程来执行任务。

以下是一个使用 QThreadPool 的示例:

#include <QCoreApplication>
#include <QThreadPool>
#include "MyRunnable.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // 获取 QThreadPool 的全局实例
    QThreadPool *pool = QThreadPool::globalInstance();
    // 创建一个自定义 QRunnable 实例
    MyRunnable *task = new MyRunnable();
    // 将任务提交给线程池
    pool->start(task);
    // 等待所有任务完成
    pool->waitForDone();
    return a.exec();
}

QThreadPool 提供了一些高级功能,如设置线程优先级、限制最大线程数以及在超时后自动销毁空闲线程等。在这里,我们将介绍这些高级功能的使用方法。

6.3.1 设置线程优先级

当将任务提交给线程池时,可以为其分配一个优先级。优先级可以是 QThread::Priority 枚举值。线程池会根据任务的优先级来决定任务的执行顺序。任务优先级高的会优先执行。

// 创建一个自定义 QRunnable 实例
MyRunnable *highPriorityTask = new MyRunnable();
// 将任务提交给线程池,设置优先级为 QThread::HighPriority
pool->start(highPriorityTask, QThread::HighPriority);
// 创建另一个自定义 QRunnable 实例
MyRunnable *lowPriorityTask = new MyRunnable();
// 将任务提交给线程池,设置优先级为 QThread::LowPriority
pool->start(lowPriorityTask, QThread::LowPriority);

6.3.2 限制最大线程数

您可以通过 QThreadPool::setMaxThreadCount() 函数限制线程池的最大线程数。这对于避免创建过多线程导致系统资源耗尽非常有用。

// 将线程池的最大线程数设置为 4
pool->setMaxThreadCount(4);

6.3.3 自动销毁空闲线程

线程池可以在一段时间后自动销毁空闲线程。要启用此功能,可以使用 QThreadPool::setExpiryTimeout() 函数设置超时时间(以毫秒为单位)。

// 将空闲线程的超时时间设置为 30000 毫秒(30 秒)
pool->setExpiryTimeout(30000);

这样,当线程在 30 秒内没有执行任务时,线程池会自动销毁这个线程,以释放系统资源。


【Qt 线程】探索Qt线程编程的奥秘:多角度深入剖析(三)https://developer.aliyun.com/article/1464091

目录
相关文章
|
3天前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
25 0
|
3月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
81 17
|
3月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
78 26
|
5月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
437 2
|
5月前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
107 10
|
6月前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
155 11
|
5月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
5月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
100 3
|
5月前
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
99 4
|
5月前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程