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にはグローバルから他に何がありますか? 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



があることは明らかです。 そして、すべてが正しく動作します。



All Articles