Qt上の単一インスタンスアプリケーション

現在、私はQtの下で積極的に執筆しています。 そして、アプリケーションを開始するときに、タスクが既に実行されているかどうかを確認するタスクを取得しました。 Linuxの下。 私は自転車をフェンスで囲みたくありませんでした。すぐにクロスプラットフォームになるように、既製のQtクラスを使用して何かを産みたいと思いました。 たとえば、Windowsには既成のソリューションがあります-ミューテックス(またはセマフォ、重要ではありません、主なものに名前が付けられています)という名前です。 彼はグーグルを始めました。 ええ、 QSystemSemaphoreと呼ばれるQtに似たようなものがあります。 次のように機能します。

QSystemSemaphoreクラスのインスタンスを作成し、グローバル(システム)セマフォを作成します
- acquire呼び出し、セマフォカウンタを1つ減らす
-何かをする
- release呼び出し、セマフォカウンタを1つ増やします

一般的に、古典的なセマフォ、グローバルのみ。 カウンタの初期値が1であるセマフォを作成し、 acquireを呼び出すと、 releaseを呼び出すまで誰もこのセマフォをキャプチャできないことは明らかです。 やった、解決策が見つかった! 単一インスタンスアプリケーションの次のコードがすぐに現れます。

-セマフォを作成する
-つかむ
-アプリケーションは動作します
-シャットダウン後、アプリケーションはセマフォを解放します

すべてが素晴らしい、すべてが素晴らしいです。 ドキュメントは、 release呼び出さなくても(たとえば、プログラムがクラッシュしたとしても)、システムはキャプチャしたすべてのユニットをセマフォに戻すことを約束します。 しかし、問題があります。 ユーザーがアプリケーションの2番目のインスタンスを起動した場合、どのようにしてそれを知るのでしょうか? acquireメソッドの呼び出しはブロックされ、セマフォが解放されるまで、アプリケーションは「ハング」し、ユーザーは信じられないような何かを待ちます。

それは見えるだろう-問題ではない。 たとえば、同じQMutex 、mutexをキャプチャするためにlockを呼び出すことに加えて、キャプチャするための非ブロック試行のための呼び出しとtry_lockを持っています。 システムのセマフォにも同様のものが必要です。 しかし、悲しいかな-彼はそうではありません。 私は長い間グーグルで検索に失敗しました-フォーラムは同様のトピックでいっぱいであり、単一の有効なソリューションではありません。 問題? 問題。 面白い? かなり。 私はそれを解決することを約束しました。

1つのQSystemSemaphore明らかに何も解決されません。 Qtにはグローバルから他に何がありますか? QSharedMemoryQSharedMemoryます。 異なるアプリケーション間の共有メモリ。 次のように機能します。

QSharedMemoryクラスのインスタンスを作成します
attachを呼び出して、作成済みのメモリに接続します
-接続が失敗した場合、 createを呼び出して共有メモリを作成します
-共有メモリを使用します(たとえば、一部のデータを別のプロセスに転送します)
-デストラクタ自体がdetachを呼び出し、共有メモリからdetachします

それは思われる-ここにある! attach呼び出しが成功した場合、アプリケーションは既に実行されています。 そうでない場合は、アプリケーションが初めて起動され、 createを呼び出しcreate 、共有メモリの所有権に関する優位性を統合します(つまり、他のすべてのattachattachは既に成功します)。 ここには実際に2つの問題があります。

最初はレースです。 2つのプロセスが同時にattachを呼び出して失敗しcreate 。どちらも最初のものであると想定し、 createを呼び出しcreate 。 1つはメモリを作成し、もう1つは失敗します。 良くありませんが、一般的には寛容です。 あなたは周りに行くことができます。

2番目の問題はより深刻です。 QSharedMemoryのLinux実装は、メモリのdetachが呼び出されない場合、 QSharedMemoryされないように設計されていることがQSharedMemoryます。 簡単に言えば、プロセスが共有メモリに参加してクラッシュした場合、メモリは作成されたままになります。 そして、次のattach呼び出しは成功します。 つまり アプリケーションの単一インスタンスの異常終了は、アプリケーションのインスタンスがこれ以上作成されないという事実につながります。 うーん。

したがって、2つのクラスQSystemSemaphoreQSharedMemory 、それぞれが必要な機能を部分的に実装しますが、最終的なタスクは解決しません。 おそらく、これら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があることは明らかです。 そして、すべてが正しく動作します。

Source: https://habr.com/ru/post/J173281/


All Articles