まず第一に:私はミューテックス/マルチスレッドプログラミングの完全な初心者なので、事前にエラーをお詫びします...
複数のスレッドを実行するプログラムがあります。スレッド(通常はCPUコアごとに1つ)は多くの計算と「思考」を行い、統計を更新する特定の(共有)メソッドを呼び出すことを決定する場合があります。統計更新の同時実行性は、ミューテックスを使用して管理されます。
stats_mutex.lock();
common_area->update_thread_stats( ... );
stats_mutex.unlock();
今問題に。それらすべてのスレッドの中で、ほとんどを必要とする1つの特定のスレッドがあります
リアルタイムの優先度。これは、実際に動作する唯一のスレッドだからです。
「ほぼリアルタイムの優先順位」とは、次のことを意味します。
スレッドt0が「特権スレッド」であり、t1 .... t15が通常のスレッドであると仮定します。
私が必要なのは:
それで、これを行うための最良の(おそらく最も簡単な)方法は何ですか?
私が考えていたのは、「privileged_needs_lock」というブール変数を持つことです。
しかし、この変数へのアクセスを管理するには、別のミューテックスが必要だと思います...これが正しい方法かどうかはわかりません...
追加情報:
どんなアイデアでも大歓迎です。ありがとう
以下の解決策は機能します(3つのミューテックスの方法):
#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;
}
提案されているように以下の解決策を試しましたが、それはしません動作します( "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;
}
スレッドプリミティブのみを使用する3つの方法を考えることができます。
ここでは、3つのミューテックスが機能します。
アクセスパターンは次のとおりです。
これにより、データへのアクセスが保護され、優先度の高いスレッドが優先度の低いスレッドよりも先にアクセスできるようになります。
これを行うための基本的な方法は、条件変数とアトミックを使用することです。
データアクセスパターン:
または、condvarで2つの非アトミックboolを使用することもできます。この手法では、ミューテックス/ condvarがフラグを保護し、データはミューテックスではなくフラグによって保護されます。
bool data_held、hpt_waiting;
優先度の低いスレッド:Mをロックし、(hpt_waitingまたはdata_held)がMでCを待機している間、data_held:= true、Mのロックを解除、{do stuff}、Mのロック、data_held:= false、Cのブロードキャスト、Mのロック解除
要求スレッドを「優先キュー」に配置します。特権スレッドは、データが空いているときに最初にデータを取得できます。
これを行う1つの方法は、ConcurrentQueues [privilegeLevel]の配列、ロック、およびいくつかのイベントを使用することです。
データを必要とするスレッドはすべてロックに入ります。データが空いている場合(ブール値)、データオブジェクトを取得し、ロックを終了します。データが別のスレッドによって使用されている場合、要求元のスレッドは、特権レベルに応じて、イベントを同時キューの1つにプッシュし、ロックを終了してイベントを待機します。
スレッドがデータオブジェクトの所有権を解放したい場合、スレッドはロックを取得し、ConcurrentQueuesの配列を最高特権の端から下に繰り返し、イベントを探します(つまり、キュー数> 0)。見つかった場合は、それを通知してロックを終了します。見つからなかった場合は、「dataFree」ブール値を設定してロックを終了します。
データへのアクセスをイベントで待機しているスレッドの準備が整うと、データオブジェクトにアクセスする場合があります。
私はそれがうまくいくはずだと思います。他の開発者の皆さん、このデザインをチェックして、レースなどを思いつくことができるかどうかを確認してください。 CZへの旅行の後、私はまだ「おもてなしの過負荷」にいくらか苦しんでいます。
編集-それらすべてに明示的なロックがあるため、おそらく並行キューさえ必要ありません。古いキューなら何でもかまいません。
#include <thread>
#include <mutex>
#include <condition_variable>
#include <cassert>
class priority_mutex {
std::condition_variable cv_;
std::mutex gate_;
bool locked_;
std::thread::id pr_tid_; // priority thread
public:
priority_mutex() : locked_(false) {}
~priority_mutex() { assert(!locked_); }
priority_mutex(priority_mutex&) = delete;
priority_mutex operator=(priority_mutex&) = delete;
void lock(bool privileged = false) {
const std::thread::id tid = std::this_thread::get_id();
std::unique_lock<decltype(gate_)> lk(gate_);
if (privileged)
pr_tid_ = tid;
cv_.wait(lk, [&]{
return !locked_ && (pr_tid_ == std::thread::id() || pr_tid_ == tid);
});
locked_ = true;
}
void unlock() {
std::lock_guard<decltype(gate_)> lk(gate_);
if (pr_tid_ == std::this_thread::get_id())
pr_tid_ = std::thread::id();
locked_ = false;
cv_.notify_all();
}
};
注意:これはpriority_mutex
不公平なスレッドスケジューリングを提供します。特権スレッドが頻繁にロックを取得する場合、他の非特権スレッドはほとんどスケジュールされていない可能性があります。
使用例:
#include <mutex>
priority_mutex mtx;
void privileged_thread()
{
//...
{
mtx.lock(true); // acquire 'priority lock'
std::unique_lock<decltype(mtx)> lk(mtx, std::adopt_lock);
// update shared state, etc.
}
//...
}
void normal_thread()
{
//...
{
std::unique_lock<decltype(mtx)> lk(mtx); // acquire 'normal lock'
// do something
}
//...
}
Linuxでは、次の男を確認できます:pthread_setschedparamおよびman sched_setscheduler
pthread_setschedparam(pthread_t thread、int policy、const struct sched_param * param);
C++ 2011についてもこれを確認してください: http://msdn.Microsoft.com/en-us/library/system.threading.thread.priority.aspx#Y78
次のようなものを試してください。クラスをスレッドセーフなシングルトンにすることも、ファンクターにすることもできます。
#include <pthread.h>
#include <semaphore.h>
#include <map>
class ThreadPrioFun
{
typedef std::multimap<int, sem_t*> priomap_t;
public:
ThreadPrioFun()
{
pthread_mutex_init(&mtx, NULL);
}
~ThreadPrioFun()
{
pthread_mutex_destroy(&mtx);
}
void fun(int prio, sem_t* pSem)
{
pthread_mutex_lock(&mtx);
bool bWait = !(pm.empty());
priomap_t::iterator it = pm.insert(std::pair<int, sem_t*>(prio, pSem) );
pthread_mutex_unlock(&mtx);
if( bWait ) sem_wait(pSem);
// do the actual job
// ....
//
pthread_mutex_lock(&mtx);
// done, remove yourself
pm.erase(it);
if( ! pm.empty() )
{
// let next guy run:
sem_post((pm.begin()->second));
}
pthread_mutex_unlock(&mtx);
}
private:
pthread_mutex_t mtx;
priomap_t pm;
};
pthreadsにはスレッドの優先順位があります:
pthread_setschedprio( (pthread_t*)(&mThreadId), wpri );
複数のスレッドがロックで待機している場合、スケジューラーは最も優先度の高いスレッドを最初にウェイクアップします。
スレッドの優先順位が機能していないため:
通常ロックと優先ロックの2つのミューテックスを作成します。
通常のスレッドは、最初に通常のロックをロックし、次に優先ロックをロックする必要があります。優先スレッドは、優先ロックをロックするだけで済みます。
Mutex mLock;
Mutex mPriLock;
doNormal()
{
mLock.lock();
pthread_yield();
doPriority();
mLock.unlock();
}
doPriority()
{
mPriLock.lock();
doStuff();
mPriLock.unlock();
}
わずかに変更ecatmur回答、複数の優先度の高いスレッドを同時に処理するために4番目のミューテックスを追加(これは必須ではなかった私の元の質問では):
#include <thread>
#include <iostream>
#include "unistd.h"
std::mutex M; //data access mutex
std::mutex N; // 'next to access' mutex
std::mutex L; //low priority access mutex
std::mutex H; //hptwaiting int access mutex
int hptwaiting=0;
void lowpriolock(){
L.lock();
while(hptwaiting>0){
N.lock();
N.unlock();
}
N.lock();
M.lock();
N.unlock();
}
void lowpriounlock(){
M.unlock();
L.unlock();
}
void highpriolock(){
H.lock();
hptwaiting++;
H.unlock();
N.lock();
M.lock();
N.unlock();
}
void highpriounlock(){
M.unlock();
H.lock();
hptwaiting--;
H.unlock();
}
void hpt(const char* s){
using namespace std;
//cout << "hpt trying to get lock here" << endl;
highpriolock();
cout << s << endl;
usleep(30000);
highpriounlock();
}
void lpt(const char* s){
using namespace std;
//cout << "lpt trying to get lock here" << endl;
lowpriolock();
cout << s << endl;
usleep(30000);
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(hpt,"high prio t6 working here");
std::thread t7(lpt,"low prio t7 working here");
std::thread t8(hpt,"high prio t8 working here");
std::thread t9(lpt,"low prio t9 working here");
std::thread t10(lpt,"low prio t10 working here");
std::thread t11(lpt,"low prio t11 working here");
std::thread t12(hpt,"high prio t12 working here");
std::thread t13(lpt,"low prio t13 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();
t8.join();
t9.join();
t10.join();
t11.join();
t12.join();
t13.join();
return 0;
}
どう思いますか?大丈夫ですか?セマフォがこの種のことをよりうまく処理できることは事実ですが、ミューテックスは私にとってはるかに管理しやすいです。