首页 > 其他分享 >QT多线程(线程互斥)

QT多线程(线程互斥)

时间:2023-06-10 19:01:20浏览次数:36  
标签:临界 互斥 死锁 线程 QMutex 多线程 资源

(文章目录)


前言

线程互斥是指在多线程并发执行时,为避免多个线程访问共享资源时发生冲突而采取的一种机制。本篇文章我们就这个问题来了解一下什么叫线程互斥,又如何解决线程互斥的问题。

一、导致问题产生的原因和解决方法

如果多个线程同时访问同一共享资源,可能会导致数据不一致、资源竞争和死锁等问题。

为了避免这些问题,可以使用互斥锁(Mutex)来保护共享资源。互斥锁是一种同步机制,用于控制多个线程对共享资源的访问。当一个线程获得了互斥锁,其他线程就无法获得该锁,直到该线程释放互斥锁为止。

二、同时访问一个临界资源带来的问题

下面我们编写一个示例程序来带大家详细的看一下同时访问一个临界资源带来的问题。

下面的代码定义了一个临时变量g_i,同时创建了一个Mythread线程,在代码中让Mythread和主线程去访问这个临界资源让这个变量增加。

static int g_i = 0;//临界资源

class Mythread : public QThread
{
protected:
    void run()
    {
        for(int i = 0; i < 5; i++)
        {
            g_i++;
            qDebug() << "Mythread g_i :" << g_i;
            sleep(1);   //休眠1s
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Mythread t1;

    t1.start();

    for(int i = 0; i < 5; i++)
    {
        g_i++;
        qDebug() << "Main Thread g_i :" << g_i;
        QThread::sleep(1);   //休眠1s
    }


    return a.exec();
}

运行结果: 可以看到这个打印结果看起来是非常奇怪的。这就是因为两个线程同时访问了一个变量导致的问题,那么下面使用锁来解决这个问题。 在这里插入图片描述

三、QMutex线程锁

QMutex是Qt框架中提供的互斥锁类,用于保护共享资源以避免多个线程同时访问同一共享资源导致的竞争问题。

QMutex的使用非常简单,基本步骤如下:

1.创建QMutex对象

QMutex mutex;

2.在访问共享资源的代码段前加锁

mutex.lock();
// Access shared resource

3.在访问共享资源的代码段后解锁

// Access shared resource
mutex.unlock();

使用线程锁解决上述的问题:

当进行访问或者使用临界资源时需要对其进行上锁操作,当使用结束后再解锁,这样就可以避免竞争同一个临界资源带来的问题。

static QMutex g_mutex;

static int g_i = 0;//临界资源

class Mythread : public QThread
{
protected:
    void run()
    {
        for(int i = 0; i < 5; i++)
        {
            g_mutex.lock();
            g_i++;
            qDebug() << "Mythread g_i :" << g_i;
            g_mutex.unlock();
            sleep(1);   //休眠1s
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Mythread t1;

    t1.start();

    for(int i = 0; i < 5; i++)
    {
        g_mutex.lock();
        g_i++;
        qDebug() << "Main Thread g_i :" << g_i;
        g_mutex.unlock();
        QThread::sleep(1);   //休眠1s
    }


    return a.exec();
}

运行结果: 在这里插入图片描述

4.线程死锁

线程死锁(Deadlock)是指两个或多个线程在执行过程中因争夺资源而造成的一种互相等待的现象,导致所有线程都被阻塞,无法继续执行。

在多线程编程中,线程死锁是一个非常常见的问题,一旦发生死锁,程序将永远无法继续执行下去,通常需要手动结束程序。线程死锁的发生通常由于多个线程之间互相等待对方释放资源,从而导致所有线程都无法执行下去。

线程死锁往往是由于以下几个因素引起的:

1.互斥:多个线程同时访问共享资源,但只能有一个线程占用该资源,其它线程必须等待。

2.不可抢占:资源在被一个线程占用时,不能被其它线程强制抢占。

3.持有和等待:一个线程持有一个资源且正在等待另外一个线程释放它所持有的资源。

4.环路等待:一组线程互相等待,并且每个线程都在等待另一个线程释放资源。

这里给出一个死锁的例子:

static QMutex g_mutex1;
static QMutex g_mutex2;

static int g_i = 0;//临界资源
static int g_i1 = 0;//临界资源

class Mythread : public QThread
{
protected:
    void run()
    {
        for(int i = 0; i < 5; i++)
        {
            g_mutex1.lock();
            g_mutex2.lock();
            g_i++;
            g_i1++;
            qDebug() << "Mythread g_i :" << g_i;
            g_mutex2.unlock();
            g_mutex1.lock();
            sleep(1);   //休眠1s
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Mythread t1;

    t1.start();

    for(int i = 0; i < 5; i++)
    {
        g_mutex2.lock();
        g_mutex1.lock();
        g_i++;
        g_i1++;
        qDebug() << "Main Thread g_i :" << g_i;
        g_mutex1.unlock();
        g_mutex2.unlock();
        QThread::sleep(1);   //休眠1s
    }


    return a.exec();
}

运行结果:

这里可以看到两个线程分别只运行了一次就卡死了。 在这里插入图片描述 程序产生死锁的原因: 主线程和被创建出来的线程开始运行并获取线程锁,主线程获取线程锁2,被创建出的线程获取线程锁1。 当被创建出的线程想获取线程锁2时会发现无法获取线程锁2,因为此时线程锁2被主线程获取了,当主线程想获取线程锁1时也是同样的道理,所有这就导致了线程的死锁。

假设线程1先获取了g_mutex1这个互斥锁,线程2先获取了g_mutex2这个互斥锁。然后线程1又试图获取g_mutex2这个互斥锁,此时它会一直等待线程2释放该互斥锁;同时,线程2也试图获取g_mutex1这个互斥锁,由于该锁已经被线程1占用,线程2也一直等待。这样,线程1和线程2就互相等待对方释放锁,导致死锁。

5.解决死锁的方法

给每一个临界资源都分配一个编号。 给每一个线程锁都分配一个编号。 一个线程锁对应一个临界资源。

每一个线程按照顺序获取线程锁。

解决代码:

QMutex g_mutex_1;
QMutex g_mutex_2;

class ThreadA : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            g_mutex_1.lock();

            qDebug() << objectName() << "get m1";

            g_mutex_2.lock();

            qDebug() << objectName() << "get m2";

            qDebug() << objectName() << "do work ...";

            g_mutex_2.unlock();
            g_mutex_1.unlock();

            sleep(1);
        }
    }
};

class ThreadB : public QThread
{
protected:
    void run()
    {
        while( true )
        {
            g_mutex_1.lock();

            qDebug() << objectName() << "get m2";

            g_mutex_2.lock();

            qDebug() << objectName() << "get m1";

            qDebug() << objectName() << "do work ...";

            g_mutex_2.unlock();
            g_mutex_1.unlock();

            sleep(1);
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    ThreadA ta;
    ThreadB tb;

    ta.setObjectName("ta");
    tb.setObjectName("tb");

    ta.start();
    tb.start();

    return a.exec();
}

总结

这篇文章讲解了线程的互斥和线程的死锁,并给出了线程死锁的解决方法。

标签:临界,互斥,死锁,线程,QMutex,多线程,资源
From: https://blog.51cto.com/u_16153875/6455190

相关文章

  • QT多线程基础
    (文章目录)前言本篇文章来讲解一下QT中的多线程使用方法。其实线程这个概念对于我们来说并不陌生,main函数在多线程中一般就被称为主线程。在QT中,使用QThread类可以方便地创建新的线程并在其中执行任务。以下介绍一些常用的QT多线程的技术和方法。一、多线程概念介绍多线程是......
  • Python多线程编程的一个掉进去不太容易爬出来的坑
    原文复制过来很多图片不能显示,发个链接吧。是使用Python+Socket编程模拟FTP工作原理的代码,多线程会引入一个坑,使用多进程不存在这个问题。原文地址 ......
  • 详解Python线程对象daemon属性对线程退出的影响
    进程、线程的概念以及多线程编程的基础知识请参考文末给出的方式在公众号历史文章中查找相关文章进行阅读。本文重点介绍线程对象daemon属性在线程退出时产生的作用和影响。首先,我们来看一下官方文档对守护线程(daemonthread)的描述:再来看一下官方文档对线程对象daemon属性的描述:可......
  • C++的多线程编程(练习一下condition_variable)
        嗯,高考结束了,那就编写一个阅卷和查成绩的多线程吧。一个线程老师阅卷,其他三个线程查成绩。代码如下:    1#include<iostream>2#include<thread>3#include<mutex>4#include<condition_variable>5#include<chrono>6#include<futu......
  • Python+tkinter+多线程实现文本自动翻页方便阅读
    任务描述:使用Python编写程序,使用tkinter创建界面,使用ScrolledText显示任意文本,然后自动上下翻页方便阅读。参考代码:运行结果:公众号“Python小屋”......
  • Python使用Queue对象实现多线程同步小案例
    queue模块的Queue对象实现了多生产者/多消费者队列,尤其适合需要在多个线程之间进行信息交换的场合,实现了多线程编程所需要的所有锁语义。Queue对象主要实现了put()和get()方法,分别用来往队列尾部追加元素和在队列头部获取并删除元素。这两个方法都允许指定超时时间,其用法分别为put(......
  • Python使用Condition对象实现多线程同步
    使用Condition对象可以在某些事件触发后才处理数据或执行特定的功能代码,可以用于不同线程之间的通信或通知,以实现更高级别的同步。在内部实现上,Condition对象总是与某种锁对象相关联。Condition对象除了具有acquire()和release()方法之外,还有wait()、wait_for()、notify()、notify_......
  • Python标准库socketserver使用线程混入实现异步TCP服务器
    功能描述:使用Python标准库socketserver中提供的ThreadingTCPServer实现异步TCP服务端程序,隐藏线程创建与管理细节,自动为每个客户端连接创建线程并处理数据。服务端代码: 客户端代码:......
  • 大家都说Java有三种创建线程的方式!并发编程中的惊天骗局!
    在Java中,创建线程是一项非常重要的任务。线程是一种轻量级的子进程,可以并行执行,使得程序的执行效率得到提高。Java提供了多种方式来创建线程,但许多人都认为Java有三种创建线程的方式,它们分别是继承Thread类、实现Runnable接口和使用线程池。但是,你们知道吗?其实在创建线程的过程中......
  • 多线程与多进程
    多线程与多进程一,什么是进程,什么是线程?​ 进程:运行中的程序.每次我们执行一个程序,咱们的操作系统对自动的为这个程序准备一些必要的资源(例如,分配内存,创建一个能够执行的线程.)​ 线程:程序内,可以直接被CPU调度的执行过程.是操作系统能够进行运算调度的最......