QSystemSemaphore
と呼ばれるQtに似たようなものがあります。 次のように機能します。
QSystemSemaphore
クラスのインスタンスを作成し、グローバル(システム)セマフォを作成します
-
acquire
呼び出し、セマフォカウンタを1つ減らす
-何かをする
-
release
呼び出し、セマフォカウンタを1つ増やします
一般的に、古典的なセマフォ、グローバルのみ。 カウンタの初期値が1であるセマフォを作成し、
acquire
を呼び出すと、
release
を呼び出すまで誰もこのセマフォをキャプチャできないことは明らかです。 やった、解決策が見つかった! 単一インスタンスアプリケーションの次のコードがすぐに現れます。
-セマフォを作成する
-つかむ
-アプリケーションは動作します
-シャットダウン後、アプリケーションはセマフォを解放します
すべてが素晴らしい、すべてが素晴らしいです。 ドキュメントは、
release
呼び出さなくても(たとえば、プログラムがクラッシュしたとしても)、システムはキャプチャしたすべてのユニットをセマフォに戻すことを約束します。 しかし、問題があります。 ユーザーがアプリケーションの2番目のインスタンスを起動した場合、どのようにしてそれを知るのでしょうか?
acquire
メソッドの呼び出しはブロックされ、セマフォが解放されるまで、アプリケーションは「ハング」し、ユーザーは信じられないような何かを待ちます。
それは見えるだろう-問題ではない。 たとえば、同じ
QMutex
、mutexをキャプチャするために
lock
を呼び出すことに加えて、キャプチャするための非ブロック試行のための呼び出しと
try_lock
を持っています。 システムのセマフォにも同様のものが必要です。 しかし、悲しいかな-彼はそうではありません。 私は長い間グーグルで検索に失敗しました-フォーラムは同様のトピックでいっぱいであり、単一の有効なソリューションではありません。 問題? 問題。 面白い? かなり。 私はそれを解決することを約束しました。
1つの
QSystemSemaphore
明らかに何も解決されません。 Qtにはグローバルから他に何がありますか?
QSharedMemory
が
QSharedMemory
ます。 異なるアプリケーション間の共有メモリ。 次のように機能します。
QSharedMemory
クラスのインスタンスを作成します
attach
を呼び出して、作成済みのメモリに接続します
-接続が失敗した場合、
create
を呼び出して共有メモリを作成します
-共有メモリを使用します(たとえば、一部のデータを別のプロセスに転送します)
-デストラクタ自体が
detach
を呼び出し、共有メモリから
detach
します
それは思われる-ここにある!
attach
呼び出しが成功した場合、アプリケーションは既に実行されています。 そうでない場合は、アプリケーションが初めて起動され、
create
を呼び出し
create
、共有メモリの所有権に関する優位性を統合します(つまり、他のすべての
attach
、
attach
は既に成功します)。 ここには実際に2つの問題があります。
最初はレースです。 2つのプロセスが同時に
attach
を呼び出して失敗し
create
。どちらも最初のものであると想定し、
create
を呼び出し
create
。 1つはメモリを作成し、もう1つは失敗します。 良くありませんが、一般的には寛容です。 あなたは周りに行くことができます。
2番目の問題はより深刻です。
QSharedMemory
のLinux実装は、メモリの
detach
が呼び出されない場合、
QSharedMemory
されないように設計されていることが
QSharedMemory
ます。 簡単に言えば、プロセスが共有メモリに参加してクラッシュした場合、メモリは作成されたままになります。 そして、次の
attach
呼び出しは成功します。 つまり アプリケーションの単一インスタンスの異常終了は、アプリケーションのインスタンスがこれ以上作成されないという事実につながります。 うーん。
したがって、2つのクラス
QSystemSemaphore
と
QSharedMemory
、それぞれが必要な機能を部分的に実装しますが、最終的なタスクは解決しません。 おそらく、これら2つの「下層階級」を調和して組み合わせることが可能でしょうか?
そしてはい!
QSystemSemaphore
は、最初の
QSharedMemory
問題であるレーシングの問題を解決します。 コード:
QSystemSemaphore sema("<Unique name1>", 1); bool isRunning; sema.acquire(); QSharedMemory shmem("<Unique name2>"); if (shmem.attach()) { isRunning = true; } else { shmem.create(1); isRunning = false; } sema.release();
簡単に言えば、グローバルセマフォは共有メモリ管理を同期します。 彼らが言うように、小学校、ワトソン。
判明した2番目の問題にも解決策があります。 かなり面白い。 共有メモリのLinux実装がそれへのリンクのカウンターをチェックしてそれを破壊するために、それ以上のリンクがなければ、単にそれを結合して切断します。 そして、アプリケーションの前のインスタンスの異常終了の結果として、(実際に)参照カウントのない起動されていない共有メモリが残っている場合、それに接続してすぐに切断すると、このメモリが破壊されます。 参照カウントがゼロ以外の場合、同じままになります。 一般に、これは必須です。
最終的なコードは次のようになります。
QSystemSemaphore sema("<Unique name1>", 1); bool isRunning; sema.acquire(); { QSharedMemory shmem("<Unique name2>"); shmem.attach(); } QSharedMemory shmem("<Unique name2>"); if (shmem.attach()) { isRunning = true; } else { shmem.create(1); isRunning = false; } sema.release();
QSharedMemory
クラスの2番目のインスタンスは、アプリケーションが終了するまで保持する
QSharedMemory
があることは明らかです。 そして、すべてが正しく動作します。