某些時候你會希望你的程式在同一時間內只會被執行一次,這需要使用 IPC 的技巧實作:第一個被執行的實體先留下某個溝通的管道,第二個以後被執行的實體就去連結這個管道,並自行決定要留下哪個實體。
最方便的方法就是用 Qt Solutions 內的元件:QtSingleApplication。但 Qt 己經不再繼續更新 Qt Solutions 了,且最近我使用它時碰到了一些詭異問題。另外要一提的是,去年我寫的 Export the symbols of QtSingleApplication 一文,現在其取得方式及編譯方式都有變動。由於其細節與本文無關,請自行參照該文。
QtSingleApplication 的使用方式如下:
QtSingleApplication 的使用方式如下:
#include <QtSingleApplication> int main( int argc, char * argv[] ) { QtSingleApplication a( argc, argv ); if( a.isRunning() ) { // popup an error message and exit return 1; } // your application part return a.exec(); }你也可以利用 QtSingleApplication::sendMessage 和 QtSingleApplication::messageReceived 來讓兩個實體間做溝通。
第二種方法是使用 QLocalServer 和 QLocalSocket 實作;事實上這也是目前 QtSingleApplication 內部的實作方式。想法很簡單:先決定一個獨特的關鍵字做為溝通的識別字,以這個識別字先連結到 local server,如果連結失敗即代表還沒人開始這個連結,那麼便自己啟動這個 local server。如此一來接著執行的其他程式都會成功連接到這個 local server,從而得知對方的存在。
使用方式也很簡單:
但要注意的是 QLocalServer 和 QLocalSocket 使用的實作方式其實是 named pipe,而在 UNIX-link 系統下,若程式沒有清理掉 named pipe 就結束的話,系統不會自動幫你清除,就導致下次執行時可能有錯誤結果。這是為什麼有上面那段醜醜的 code 片段的原因。
而 Windows 的問題則是同一個 named pipe 可以同時有數個程式去監聽它,但因為上述的使用方式在同一時間內只會有一個實體開啟 local server ,所以倒不是什麼大問題。
另外,雖然 named pipe 好像跟網路無關, QLocalServer 和 QLocalSocket 依然被歸類在 QtNetwork 模組內。
使用方式也很簡單:
#include <QtNetwork/QLocalServer> #include <QtNetwork/QLocalSocket> #include <QtGui/QApplication> int main( int argc, char * argv[] ) { QApplication a( argc, argv ); QLocalSocket c; c.connectToServer( "KeyWord" ); if( c.waitForConnected( 100 ) ) { // 0.1 秒後逾時,因為是連到本地端,其實用不了 0.1 秒那麼久 // 回傳 true 代表連結成功,己有其他實體存在 c.disconnectFromServer(); return 1; } QLocalServer s; while( !s.listen( "KeyWord" ) ) { // hack for unix-like system if( s.serverError() == QAbstractSocket::AddressInUseError && QLocalServer::removeServer( "KeyWord" ) ) { continue; } // error on listening return 1; } // your application part // please call s.close() before application exits. return a.exec(); }這個方法的好處為因為是 Server/Client 架構,可以很方便地和多個程式同時溝通。
但要注意的是 QLocalServer 和 QLocalSocket 使用的實作方式其實是 named pipe,而在 UNIX-link 系統下,若程式沒有清理掉 named pipe 就結束的話,系統不會自動幫你清除,就導致下次執行時可能有錯誤結果。這是為什麼有上面那段醜醜的 code 片段的原因。
而 Windows 的問題則是同一個 named pipe 可以同時有數個程式去監聽它,但因為上述的使用方式在同一時間內只會有一個實體開啟 local server ,所以倒不是什麼大問題。
另外,雖然 named pipe 好像跟網路無關, QLocalServer 和 QLocalSocket 依然被歸類在 QtNetwork 模組內。
第三種方式,就是使用 QSharedMemory。基本上這是一個比較難駕馭的方法,因為並沒有比較有效的方式去偵測是否有新的程序要溝通。簡單的流程如下:使用一個獨特的關鍵字當識別字,向系統要求一塊 shared memory 並寫入值,如果失敗的話就代表己經有其他程序正在使用。但如果要進一步做溝通,接下來的部分就麻煩了。
單純的作法:
雖然 QSharedMemory 在解構時會幫你釋放 shared memory,但若是因為某些原因而沒有執行到解構子,在 UNIX-like 系統下一樣不會幫你回收,你必須要自行想辦法回收。
單純的作法:
#include <QtCore/QSharedMemory> #include <QtGui/QApplication> int main( int argc, char * argv[] ) { QApplication a( argc, argv ); QSharedMemory s( "KeyWord" ); if( s.attach() ) { // shared memory already exists return 1; } if( !s.create( 1 ) ) { // shared memory creation failed return 1; } // your application part return a.exec(); }因為 QSharedMemory 本身並沒有任何檢查 shared memory 是否有變動的機制,因此若要溝通可能就需要 QTimer 去做定期更新。讀寫時也請記得配合 QSharedMemory::lock 和 QSharedMemory::unlock 使用,否則會有競速問題。
雖然 QSharedMemory 在解構時會幫你釋放 shared memory,但若是因為某些原因而沒有執行到解構子,在 UNIX-like 系統下一樣不會幫你回收,你必須要自行想辦法回收。
以上提到的方法各位可以依自己的需求選用。我個人目前是使用第二種方法,並在連結成立時送出 magic number 來識別是否連接到錯誤的程式。
其实libunique不错…还帮你处理了启动反馈等等杂事……
回覆刪除不过应该和qt不和……
well, 畢竟主題是 Qt 囉
回覆刪除libunique 不只使用了 GLib 還用上了 Gtk+
也許哪天我想不開去寫個 Gtk+ 應用就會用上了吧