如何在互斥锁定中优先使用特权线程?

d3k*_*d3k 24 c++ multithreading mutex c++11

首先:我完全是互斥/多线程编程的新手,所以对于任何错误提前抱歉...

我有一个运行多个线程的程序.线程(通常每个cpu核心一个)进行大量计算和"思考",然后有时他们决定调用更新某些统计信息的特定(共享)方法.通过使用互斥锁来管理统计信息更新的并发性:

stats_mutex.lock();
common_area->update_thread_stats( ... );
stats_mutex.unlock();
Run Code Online (Sandbox Code Playgroud)

现在来问题了.在所有这些线程中,有一个特定的线程几乎需要
实时优先级,因为它是唯一实际运行的线程.

"几乎是实时优先"的意思是:

假设线程t0是"特权者"而t1 .... t15是正常的.现在发生的是:

  • 线程t1获得锁定.
  • 线程t2,t3,t0调用lock()方法并等待它成功.
  • 线程t1调用unlock()
  • 线程t2,t3,t0中的一个(随机地,据我所知)成功获得锁定,而其他线程继续等待.

我需要的是:

  • 线程t1获得锁定.
  • 线程t2,t3,t0调用lock()方法并等待它成功.
  • 线程t1调用unlock()
  • 线程t0获得锁定,因为它具有特权

那么,做这件事的最好(可能是最简单的)方法是什么?

我想的是有一个名为"privileged_needs_lock"的bool变量.

但我想我需要另一个互斥锁来管理对这个变量的访问...我不知道这是不是正确的方法......

附加信息:

  • 我的线程使用C++ 11(从gcc 4.6.3开始)
  • 代码需要在Linux和Windows上运行(但目前仅在Linux上进行测试).
  • 锁定机制的性能不是问题(我的性能问题是在内部线程计算中,并且线程数总是很低,每个cpu核心最多一个或两个)

任何想法都表示赞赏.谢谢


以下解决方案有效(三个互斥方式):

#include <thread>
#include <iostream>
#include "unistd.h"

std::mutex M;
std::mutex N;
std::mutex L;

void lowpriolock(){
  L.lock();
  N.lock();
  M.lock();
  N.unlock();
}

void lowpriounlock(){
  M.unlock();
  L.unlock();
}

void highpriolock(){
  N.lock();
  M.lock();
  N.unlock();
}

void highpriounlock(){
  M.unlock();
}

void hpt(const char* s){
  using namespace std;
  //cout << "hpt trying to get lock here" << endl;
  highpriolock();
  cout << s << endl;
  sleep(2);
  highpriounlock();
}

void lpt(const char* s){
  using namespace std;
  //cout << "lpt trying to get lock here" << endl;
  lowpriolock();
  cout << s << endl;
  sleep(2);
  lowpriounlock();
}

int main(){
std::thread t0(lpt,"low prio t0 working here");
std::thread t1(lpt,"low prio t1 working here");
std::thread t2(hpt,"high prio t2 working here");
std::thread t3(lpt,"low prio t3 working here");
std::thread t4(lpt,"low prio t4 working here");
std::thread t5(lpt,"low prio t5 working here");
std::thread t6(lpt,"low prio t6 working here");
std::thread t7(lpt,"low prio t7 working here");
//std::cout << "All threads created" << std::endl;
t0.join();
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
return 0;
}
Run Code Online (Sandbox Code Playgroud)

尝试按照建议尝试下面的解决方案,但它不起作用(使用"g ++ -std = c ++ 0x -o test test.cpp -lpthread"编译):

#include <thread>
#include <mutex>

#include "time.h"
#include "pthread.h"

std::mutex l;

void waiter(){
  l.lock();
  printf("Here i am, waiter starts\n");
  sleep(2);
  printf("Here i am, waiter ends\n");
  l.unlock();
}

void privileged(int id){
  usleep(200000);
  l.lock();
  usleep(200000);
  printf("Here i am, privileged (%d)\n",id);
  l.unlock();  
}

void normal(int id){
  usleep(200000);
  l.lock();
  usleep(200000);
  printf("Here i am, normal (%d)\n",id);
  l.unlock();    
}

int main(){
  std::thread tw(waiter);
  std::thread t1(normal,1);
  std::thread t0(privileged,0);
  std::thread t2(normal,2);

  sched_param sch;
  int policy; 

  pthread_getschedparam(t0.native_handle(), &policy, &sch);
  sch.sched_priority = -19;
  pthread_setschedparam(t0.native_handle(), SCHED_FIFO, &sch);

  pthread_getschedparam(t1.native_handle(), &policy, &sch);
  sch.sched_priority = 18;
  pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch);

  pthread_getschedparam(t2.native_handle(), &policy, &sch);
  sch.sched_priority = 18;
  pthread_setschedparam(t2.native_handle(), SCHED_FIFO, &sch);

  tw.join();
  t1.join();
  t0.join();
  t2.join();

  return 0;  
}
Run Code Online (Sandbox Code Playgroud)

eca*_*mur 37

我可以想到只使用线程原语的三种方法:

三重互斥

三个互斥量可以在这里工作:

  • 数据互斥('M')
  • next-to-access互斥锁('N'),和
  • 低优先级访问互斥锁('L')

访问模式是:

  • 低优先级线程:锁定L,锁定N,锁定M,解锁N,{执行填充},解锁M,解锁L
  • 高优先级线程:锁定N,锁定M,解锁N,{做东西},解锁M.

这样,对数据的访问受到保护,并且高优先级线程可以在访问它的低优先级线程之前.

互斥,条件变量,原子旗

执行此操作的基本方法是使用条件变量和原子:

  • Mutex M;
  • Condvar C;
  • atomic bool hpt_waiting;

数据访问模式:

  • 低优先级线程:锁定M,同时(hpt_waiting)等待C on M,{do stuff},广播C,解锁M
  • 高优先级线程:hpt_waiting:= true,锁定M,hpt_waiting:= false,{do stuff},广播C,解锁M

互斥,条件变量,两个非原子旗

或者你可以使用两个带有condvar的非原子bool; 在这种技术中,互斥锁/ condvar保护标志,数据不受互斥锁保护,而是受标志保护:

  • Mutex M;
  • Condvar C;
  • bool data_held,hpt_waiting;

  • 低优先级线程:锁定M,同时(hpt_waiting或data_held)等待C on M,data_held:= true,解锁M,{do stuff},锁定M,data_held:= false,广播C,解锁M

  • 高优先级线程:锁定M,hpt_waiting:= true,while(data_held)等待C on M,data_held:= true,{do stuff},锁定M,data_held:= false,hpt_waiting:= false,广播C,解锁M

  • 两种方法(ecatmur 和 Martin James)都可以。3互斥方式更简单,更容易实现,只要这样做就可以了。优先级队列更加灵活,但在编码时需要更多关注,因此仅当我需要特定功能时我才会实现它。 (3认同)
  • 为什么不: 高优先级:lock(M)--&gt;{do stuff}--&gt;unlock(M) 低优先级:lock(N)--&gt;lock(M)--&gt;{do stuff}--&gt;unlock( M)--&gt;解锁(N) ? (2认同)

Mar*_*mes 7

将请求线程放在"优先级队列"上.特权线程可以在数据空闲时首先获取数据.

一种方法是使用ConcurrentQueues [privilegeLevel]数组,一个锁和一些事件.

任何想要数据的线程都会进入锁定状态.如果数据是空闲的(布尔值),它将获取数据对象并退出锁定.如果数据正由另一个线程使用,则请求线程将事件推送到其中一个并发队列,具体取决于其权限级别,退出锁并等待事件.

当一个线程想要释放它对数据对象的所有权时,它获取锁并从最高权限端向下迭代ConcurrentQueues数组,寻找一个事件(即队列计数> 0).如果它找到一个,它会发出信号并退出锁,如果没有,它会设置'dataFree'布尔值并退出锁.

当等待事件访问数据的线程准备就绪时,它可以访问数据对象.

我认为应该工作.请其他开发人员检查这个设计,看看你是否能想到任何比赛等?去CZ旅行后,我仍然受到"好客超载"的影响.

编辑 - 可能甚至不需要并发队列,因为它们全部显式锁定.任何旧队列都可以.