QT多线程(线程互斥)


前言

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

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

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

为了避免这些问题,可以使用互斥锁(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();
}

总结

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

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Qt线程提供了支持,基本形式有独立于平台的线程类、线程安全方式的事件传递和一个全局Qt互斥量允许你可以从不同的线程调用Qt方法。 这个文档是提供给那些对多线程编程有丰富的知识和经验的听众的。推荐阅读: Threads Primer: A Guide to Multithreaded Programming Thread Time: The Multithreaded Programming Guide Pthreads Programming: A POSIX Standard for Better Multiprocessing (O'Reilly Nutshell) Win32 Multithreaded Programming 警告:所有的GUI类(比如,QWidget和它的子类),操作系统核心类(比如,QProcess)和网络类都不是线程安全的。 QRegExp使用一个静态缓存并且也不是线程安全的,即使通过使用QMutex来保护的QRegExp对象。 启用线程支持 在Windows上安装Qt时,在一些编译器上线程支持是一个选项。 在Mac OS X和Unix上,线程支持可以当你在运行configure脚本时添加-thread选项就可以生效了。在Unix平台上,多线程程序必须用特殊的方式连接,比如使用特殊的libc,安装程序将会创建另外一个库libqt-mt并且因此线程程序必须和这个库进行连接(使用-lqt-mt)而不是标准的Qt库。 在两个平台上,你都应该定义宏QT_THREAD_SUPPORT来编译(比如,编译时使用-DQT_THREAD_SUPPORT)。在Windows上,这个通常可以在qconfig.h写一个条目来解决。 线程类 最重要的类是QThread,也就是说要开始一个新的线程,就是开始执行你重新实现的QThread::run()。这和Java的线程类很相似。 为了写线程程序,在两个线程同时希望访问同一个数据时,对数据进行保护是很必要的。因此这里也有一个QMutex类,一

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花落已飘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值