您的位置:首页 > 编程语言 > Qt开发

Qt修炼手册12_线程同步与线程等待条件

2017-03-09 15:29 330 查看

1.同步线程:何为同步?

参考百度百科:

1.1 线程

线程是进程中的一个实体,是被系统独立调度和分配的基本单位。一个进程可以有多个线程,一个线程必须有一个父进程,线程自己不拥有系统资源,只有运行必须的一些数据结构,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源,一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

1.2 多线程

由于线程之间的相互制约,致使线程在运行中呈现出间断性,线程也有就绪、阻塞、和运行3种基本状态,所以,在一个进程中可以创建几个线程来提高程序的执行效率,并且有些程序还通过采用多线程技术来同事执行多个不同的代码模块。 

在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步

1.3 线程同步

即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,目前实现线程同步的方法有很多,临界区对象就是其中一种。

临界区的使用步骤:

1.初始化一个CURITY_ATTRIBUTES结构,在临界区对象之前,需要定于全局CURITY_ATTRIBUTES结构变量,在调用CreateThread函数前调用InitializeCriticalSection(LPC RITICAL_SECTION lpCriticalSection)函数初始化临界区对象。 

2.申请进入一个临界区。在线程函数中要对保护的数据进行操作前,可以通过调用 EnterCriticalSection(LPC RITICAL_SECTION lpCriticalSection)函数申请进入临界区,由于在同一时间内,只允许一个线程进入临界区,所以在申请的时候如果有一个线程进入到临界区,则该函数就会一直等到那个线程执行完临界区代码。 

3.离开临界区。当执行完临界区代码后,需要调用LeaveCriticalSection(LPC RITICAL_SECTION lpCriticalSection)函数把临界区交还给系统。 

4.删除临界区,当不需要临界区是可以调用DeleteCriticalSection(LPC RITICAL_SECTION lpCriticalSection)函数将临界区对象删除。

1.4 怎么理解同步?

同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。“同”字从字面上容易理解为一起动作其实不是,“同”字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin,
isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。

在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

2.线程等待条件

应用程序运行多线程时,无法保证哪个线程先运行,等待条件就等于同步线程。
例如,假如应用程序中有两个线程同时运行。第一个线程进行工作,第二个线程则处于待机状态,直到第一个线程完成到一定程度立即调用第二个线程,第二个线程才会开始工作。很明显这就是协同~
使用等待条件可以实现上述线程间的同步。为了使用等待条件,Qt提供了QWaitCondition类。QWaitCondition类使用函数wait()使线程进入阻塞状态,使用waitOne()或waitAll()则可以讲线程从阻塞状态唤醒。
需要注意的是,QWaitCondition在线程中与互斥体一起使用。
#include <QtWidgets/QApplication>
#include <QWaitCondition>
#include <QMutex>
#include <QThread>

QMutex mutex;              //各线程的全局量
QWaitCondition incNumber;  //各线程的全局量
int numUsed;               //producer 和 consumer 的共享变量

class Producer : public QThread
{
public:
Producer() {}
protected:
void run()
{
for(int i = 0 ; i < 10 ; i++){
//sleep(1);
mutex.lock();  //注意此互斥量生产者与消费者共享
++numUsed;
incNumber.wakeAll();
qDebug("Producer-numUsed : %d", numUsed);
mutex.unlock();
}
}
};

class Consumer : public QThread
{
public:
Consumer(){}
protected:
void run()
{
for(int i = 0 ; i < 10 ; i++)
{
mutex.lock();
//incNumber.wait(&mutex);  //此处mutex与生产者是同一个,所以会等待生产者线程
qDebug("Consumer-numUsed : %d", numUsed);
mutex.unlock();        }
}
};

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

Producer producer;
Consumer consumer;
producer.start();
consumer.start();

return a.exec();
}

//Sleep(1);//incNumber.wait(&mutex); 输出结果:



生产者消费者线程同时处于激活状态。尽在互斥量作用下交替抢占资源。

sleep(1); //incNumber.wait(&mutex);输出结果:



Producer::run()函数被sleep(1)拖延,所以,在拖延期间,线程Comsumer::run()抢占资源,直至完成操作。

sleep(1); incNumber.wait(&mutex);输出结果:



这个和第一个结果相同,但是执行原理却不同。第一个实验是线程Producer和线程Comsumer交替抢占资源。而该实验实现的是线程等待条件。
线程Producer比较耗时(空循环),线程Consumer内加入线程继续执行的等待条件。该过程可以描述为:
等待2s->Producer1->Consumer1...(暂时猜测等待超时,会强制执行)
调试代码:
for(int i = 0 ; i < 10 ; i++){
sleep(3);
mutex.lock();  //注意此互斥量生产者与消费者共享
++numUsed;
incNumber.wakeAll();
qDebug("Producer-numUsed : %d", numUsed);
mutex.unlock();
mutex.lock();
incNumber.wait(&mutex);  //此处mutex与生产者是同一个,所以会等待生产者线程
sleep(1);
qDebug("Consumer-numUsed : %d", numUsed);
mutex.unlock();        }

//sleep(1);  incNumber.wait(&mutex);输出结果:



该实验与实验三是一个对比实验。在没有延时情况下,不会启动强行执行。所以执行流程应该为:
Producer1->Producer1(customer唤醒但执行等待)->Customer1(等待结束,执行局部域)......
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: