qt延时执行类,也可以在子线程中更改UI

本文介绍了如何使用C++和Qt库创建一个延迟任务调度器,通过定时器按执行时间顺序执行回调函数,并在主线程与子线程间处理UI更新。关键点包括std::function、std::list和std::mutex的运用。
#include <functional>
#include <mutex>
#include <list>
class DelayTask
{
	struct Task
	{
		int64_t time; 
		std::function<void()> func;
	};
public:
	static DelayTask* ins();
	void addTask(int millseconds, std::function<void()>&& func);
protected:
	DelayTask() ;
	std::list<Task> tasks;//不用map因为两个任务可能时间相同而冲突
	std::mutex mtx;
	void run();
};


#include <QTimer>
DelayTask* DelayTask::ins()
{
	static DelayTask instance;
	return &instance;
}

DelayTask::DelayTask()
{
	auto t = new QTimer();
	QObject::connect(t, &QTimer::timeout, [this]() {run(); });
	t->start(100);
}

void DelayTask::addTask(int millseconds, std::function<void()>&& func)
{
	std::lock_guard<std::mutex> LockGuard(mtx);
	int64_t time = clock() + millseconds;
	for (auto i = tasks.begin(); i != tasks.end(); i++)
	{
		if (i->time > time)
		{
			tasks.insert(i, Task{ time,std::move(func) });
			return;
		}
	}
	tasks.emplace_back(Task{ time, std::move(func) });
}

void DelayTask::run()
{
	std::lock_guard<std::mutex> LockGuard(mtx);
	int64_t time = clock();
	for (auto i = tasks.begin(); i != tasks.end(); i++)
	{
		if (i->time <= time)
		{
			i->func();
			i = tasks.erase(i);
			if (i == tasks.end())
				return;
		}
		else
			return;
	}
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
	auto l1 = new QLabel("0");
	l1->show();
	DelayTask::ins()->addTask(2000, [=]() {
		l1->setText("2000");
		});
	QtConcurrent::run([=]() {
		_sleep(4000);//子线程处理中
		//l1->setText("4000");//会崩溃
		DelayTask::ins()->addTask(0, [=]() {
			l1->setText("4000");
			});
		});
	return a.exec();
}

用定时器更改ui,保存一个按执行时间排序的链表。

### QT 线程中的延时函数及其常见问题解决方案 在Qt框架下,当涉及到多线程编程以及需要在线程内部执行延时操作时,可能会遇到一些特定的问题。为了确保应用程序稳定性和响应性,在不同场景下应采用合适的方法。 #### 使用 `QThread` 和非阻塞方式实现安全延时 对于带有图形界面的应用程序来说,直接在一个重要工作线程(如主线程/UI线程)上使用简单的sleep机制会引发UI冻结等问题[^2]。因此推荐的做法是在子线程中利用事件循环配合定时器(`QTimer`)完成延时效果: ```cpp // 创建一个新的线程对象 QThread* workerThread = new QThread; // 定义一个工作者 Worker 继承 QObject 并重载 run 方法 class Worker : public QObject { Q_OBJECT public slots: void doWork(int delayMs){ qDebug()<<"Working thread ID:"<<QThread::currentThreadId(); QTimer timer; connect(&timer,SIGNAL(timeout()),this,SLOT(handleTimeout())); timer.start(delayMs); exec(); // 开启事件循环 qDebug()<< "Finished work"; } private slots: void handleTimeout(){ qDebug()<<"Time out!"; quit(); // 结束事件循环 } }; Worker *workerObj = new Worker(); // 将工作者移到新线程并启动该线程 workerObj->moveToThread(workerThread); connect(workerThread,&QThread::started,[=]()mutable{workerObj->doWork(1000);});// 设置延迟时间为1秒 workerThread->start(); ``` 上述代码展示了如何创建独立的工作线程,并通过连接到`QTimer`的超时信号来代替传统的睡眠功能,从而避免了因长时间占用CPU而导致其他任务无法正常调度的情况发生。 #### 处理跨线程访问资源的安全性 考虑到多个线程可能同时尝试修改共享数据结构或资源,这可能导致竞态条件或其他同步错误。为此可以引入互斥量(Mutex),例如`QMutex`,以保护临界区内的关键操作;或者更进一步考虑高级别的并发控制工具比如`QSemaphore`、`QWaitCondition`等[^3]。 另外一种常见的做法是借助于信号槽机制传递消息给目标线程而不是直接调用其成员方法,这样不仅能够保持良好的解耦设计模式,而且天然支持异步通信模型。 #### 避免误用全局静态变量作为线程间通讯手段 有时开发者倾向于依赖全局静态变量来进行简单快速的数据交换,但这往往会在复杂环境下埋下隐患——特别是当这些变量被频繁更新且缺乏必要的锁定措施之时。更好的实践是从一开始就规划好清晰的服务接口和服务发现协议,让各个组件之间仅限于必要限度上的交互。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值