指定线程在特定核心

1原博部分:

转自   http://bukkake.iteye.com/blog/688383

今天遇到这么一个问题,有一个任务分上下行,都是计算复杂型的,如何保证用线程可以让他们运行在双核机器上的不同核心上充分利用CPU?

     我当时想了一下,认为线程的分配是操作系统处理的,如果足够精明的话,可以将线程合理分配。不过我回去查了查资料,竟然还真能指定CPU,就是用SetThreadAffinityMask,当然我也看到对此函数的批评,但实际运行效果确实还可以。
C代码  收藏代码
  1. #include <windows.h>  
  2. #include <stdio.h>  
  3. #define ThreadCount 2  
  4.   
  5. DWORD WINAPI func(LPVOID pvoid)  
  6. {  
  7.     while(1)  
  8.         ;  
  9.     return 0;  
  10. }  
  11.   
  12. int main()  
  13. {  
  14.     HANDLE threads[ThreadCount];  
  15.     int i;  
  16.     for (i = 0; i < ThreadCount; i++)  
  17.     {  
  18.         threads[i] = CreateThread(NULL, 0, func, NULL, 0, NULL);  
  19.         SetThreadAffinityMask(threads[i], 1<<i);  
  20.         if (threads[i] == NULL)  
  21.         {  
  22.             OutputDebugString(L"CreateThread Error!\n");  
  23.             exit(1);  
  24.         }  
  25.     }  
  26.     getchar();  
  27.     return 0;  
  28. }  

    根据自己机器不同核心数调整一下常量,就可以观察cpu们依次到达100%了。

    有兴趣的话还可以看SetThreadAffinityMask的返回值,其意义是该线程原先的亲缘性,我机器上返回是15,也就是00001111,大致也可以猜出意思是可以在四核上任意分配。

增加部分:

具体函数为:
1
DWORD_PTR SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
其中,第一个参数为线程句柄,第二个参数为一个mask。
如果要知道当前线程的句柄,可以通过函数:GetCurrentThread()得到。否则,在创建多线程的时候,也同样可以得到创建的线程的句柄。
第二个参数为mask,可取值为0~2^31(32位)和0~2^63(64位),每一位代表每一个CPU是否使用。
比如,你要指定进程到第0个CPU上,则mask=0×01
第1个CPU:mask=0×02
第2个CPU:mask=0×04 (注意不是0×03)
第3个CPU:mask=0×08
以此类推。
如果要指定多个CPU:
比如第0、1个:mask=0×03
第1、2个:mask=0×06
以此类推。
如果CPU个数不足,则会进行取模操作。比如一共4个CPU,则mask=0×0010则和0×01一样。



<think>我们正在讨论Qt中如何确保槽函数在指定线程中执行的线程安全。根据前面的对话,用户已经了解了基本的线程安全机制(如互斥锁、读写锁等),现在用户想了解槽函数在指定线程中执行的方法。 根据引用[2]中的内容,Qt中信号槽的连接方式决定了槽函数在哪个线程执行: - 直接连接(Qt::DirectConnection):槽函数在信号发出者所在的线程中执行。 - 队列连接(Qt::QueuedConnection):槽函数在信号接收者所在的线程中执行。 - 自动连接(Qt::AutoConnection):如果信号发射者和接收者在同一个线程,则等同于直接连接;否则等同于队列连接。 因此,要确保槽函数在指定线程中执行,我们可以: 1. 将接收者对象移动到目标线程(使用`QObject::moveToThread()`)。 2. 使用队列连接(Qt::QueuedConnection)来连接信号和槽。 这样,当信号发出时,如果接收者对象不在当前线程,信号将会被放入接收者所在线程的事件循环中,然后由该线程执行槽函数。 同时,引用[1]提到,我们可以将槽函数放在一个QThread子类中,并将该线程与信号连接起来。这实际上就是利用Qt的事件循环机制和线程间通信的队列连接方式。 具体步骤: 1. 创建一个工作线程(可以继承QThread,或者使用moveToThread方式)。 2. 将包含槽函数的对象移动到该线程(使用`moveToThread`)。 3. 使用队列连接方式(或者自动连接,因为跨线程会自动使用队列连接)连接信号和槽。 示例代码(使用moveToThread方式): ```cpp #include <QCoreApplication> #include <QObject> #include <QThread> #include <QDebug> // 工作类,包含槽函数 class Worker : public QObject { Q_OBJECT public slots: void doWork() { qDebug() << "Work in thread:" << QThread::currentThreadId(); } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建工作者和线程 Worker worker; QThread workerThread; // 将worker移动到新线程 worker.moveToThread(&workerThread); // 线程开始时执行某个操作(这里我们使用直接连接触发工作,但注意:直接连接在worker所在线程执行,而worker已移动,所以用队列连接) // 但注意:started信号在workerThread线程中发出,而worker已经移动到该线程,因此使用自动连接就是直接连接,槽函数会在workerThread线程中执行。 QObject::connect(&workerThread, &QThread::started, &worker, &Worker::doWork, Qt::QueuedConnection); // 启动线程 workerThread.start(); return a.exec(); } ``` 注意:上面的连接方式我们使用了`Qt::QueuedConnection`,但实际上,由于worker已经移动到了workerThread线程,而信号`started`是在workerThread线程中发出的(即信号发出者和接收者在同一个线程),所以自动连接会变成直接连接,槽函数立即执行。但为了确保跨线程时也是队列连接,我们可以显式指定连接方式。 另外,如果我们从其他线程(比如主线程)向worker对象发送信号,那么使用队列连接就可以确保槽函数在workerThread线程中执行。 例如,我们在主线程中通过一个按钮点击信号来触发: ```cpp // 在主窗口中 // 假设有一个按钮,点击后触发工作 connect(ui->pushButton, &QPushButton::clicked, &worker, &Worker::doWork, Qt::QueuedConnection); ``` 这样,无论点击按钮的信号在哪个线程(主线程)发出,worker的doWork槽函数都会在workerThread线程中执行。 总结: 确保槽函数在指定线程执行的关键步骤: - 将槽函数所在的对象移动到目标线程(使用`moveToThread`)。 - 使用队列连接(Qt::QueuedConnection)连接信号和槽。如果信号和槽在不同线程,自动连接也会使用队列连接。 注意事项: - 对象在创建后,只能在创建它的线程中移动。也就是说,在主线程创建的对象,可以移动到子线程;在子线程创建的对象,也可以移动到其他线程(但要注意线程退出时的资源释放)。 - 移动后,该对象的事件循环将在目标线程中处理,包括信号槽调用(队列连接)和定时器事件等。 - 不要在该对象的方法中直接调用需要目标线程上下文的功能(比如界面更新),因为界面元素通常只能在主线程更新。 对于线程安全,如果槽函数访问共享资源,即使槽函数在指定线程中执行,如果多个线程可能同时访问该资源,仍然需要使用互斥锁等机制保护。 相关问题: 1. 如何将对象移动到另一个线程?有哪些注意事项? 2. 信号槽的队列连接和直接连接有什么区别? 3. 自动连接(Qt::AutoConnection)在什么情况下会使用队列连接? 4. 在多线程环境下,如何安全地退出线程并释放资源? </think>### 确保槽函数在指定线程中执行的方法 在Qt中,可通过以下方法确保槽函数在特定线程中执行: #### 1. 使用`moveToThread`方法(推荐) 将槽函数所属对象移动到目标线程,使用队列连接(Qt::QueuedConnection)确保槽函数在目标线程执行: ```cpp // 创建工作者对象和线程 Worker* worker = new Worker; QThread* workerThread = new QThread; // 将worker移动到新线程 worker->moveToThread(workerThread); // 连接信号槽(必须使用队列连接) connect(sender, &Sender::signal, worker, &Worker::slotFunction, Qt::QueuedConnection); // 启动线程 workerThread->start(); ``` 此方法的核心是: - `moveToThread`改变对象的事件循环所属线程[^1] - `Qt::QueuedConnection`确保信号被放入目标线程的事件队列[^2] #### 2. 自定义QThread子类 通过重写QThread的run()方法控制执行上下文: ```cpp class WorkerThread : public QThread { Q_OBJECT protected: void run() override { // 此处创建的槽函数将在本线程执行 Worker worker; connect(sender, &Sender::signal, &worker, &Worker::slotFunction); exec(); // 启动事件循环 } }; // 使用示例 WorkerThread thread; thread.start(); ``` #### 3. 显式指定连接类型 创建连接时显式指定连接方式: ```cpp // 队列连接:槽函数在接收者所属线程执行 connect(sender, SIGNAL(signal()), receiver, SLOT(slot()), Qt::QueuedConnection); // 阻塞队列连接:线程安全的消息队列 connect(sender, SIGNAL(signal()), receiver, SLOT(slot()), Qt::BlockingQueuedConnection); ``` #### 关键注意事项: 1. **对象父子关系**: 在父对象之前创建子线程对象,避免父对象销毁时子线程未结束 2. **线程退出处理**: 使用`thread.quit()`和`thread.wait()`安全终止线程 3. **跨线程资源访问**: 即使槽函数在目标线程执行,访问共享资源仍需同步机制: ```cpp void Worker::slotFunction() { QMutexLocker locker(&mutex); // 加锁保护共享资源 sharedResource.doSomething(); } ``` 4. **UI线程限制**: 所有GUI操作必须位于主线程,可通过`QMetaObject::invokeMethod`委托: ```cpp QMetaObject::invokeMethod(uiObject, "updateUI", Qt::QueuedConnection); ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值