使用libxml2庫的朋友,可能會對它提供的初始化介面(xmlInitParser )和清除資源介面(xmlCleanupParser )感到困惑.因為在它首頁中提供的例子裡面,各處使用的情況差別很大. 我花了些時間把這兩個介面使用方法整理如下:
1. 在單線程(single-threaded)環境中
xmlInitParser 可以被調用一次,或者被調用多次(多於一次),甚至可以不被調用.
對於第一種情況,很容易理解,因為介面作用是初始化,而且應該在程式的入口處調用. 這也是推薦的使用方法.
對於第二種情況,看其介面的實現代碼就很容易理解,第二次以後的調用只是判斷完標誌位後簡單地返回.
--------------------------------------------------------------------------------
void xmlInitParser(void) {
if (xmlParserInitialized != 0)
return;
//do initialization for xml library
…
xmlParserInitialized = 1;
}
--------------------------------------------------------------------------------
對於第三種情況,在作者在mailing list的回答中可找到答案,而且這一點我也已經簡單地通過例子驗證過.而且在libmxl2內建的很多sample中,都是屬於這樣的情況.
--------------------------------------------------------------------------------
http://mail.gnome.org/archives/xml/2003-May/msg00027.html
Q: 1. If I *don't* use libxml2's thread-support do I have to call xmlInitParser() only once per application or once per parsing?
A: You don't even need to call it. It's done automatically, it's just better to do it explicitly in a thread environment.
--------------------------------------------------------------------------------
類似於xmlInitParser(), xmlCleanupParser()也可以被調用一次,或者被調用多次(多於一次),甚至可以不被調用.
對於第一種情況,很容易理解,因為介面作用是清除資源的, 而且應該在程式的出口處調用. 這也是推薦的使用方法.
對於第二種情況,看其介面的實現代碼就很容易理解,第二次以後的調用只是判斷完標誌位後簡單地返回.
--------------------------------------------------------------------------------
void xmlCleanupParser(void) {
if (!xmlParserInitialized) return;
// do cleanup for xml library … xmlParserInitialized = 0;
}
--------------------------------------------------------------------------------
對於第三種情況,這樣的使用方法不會對程式造成任何的破壞,但是在xmlInitParser()中分配的部分記憶體將一直被佔用,直至整個程式退出.所以這是一種不會產生影響但也不推薦的使用方法.
2. 在多線程(multi-threaded)環境中
在多線程環境下,使用它們要比單線程環境下面需要注意更多的問題.
使用xmlInitParser()必須遵循一下兩個原則:
(1) xmlInitParser()不能線上程中被調用,因為xmlInitParser()不是原子操作,可能會引起線程競爭,導致程式意外.
(2) xmlInitParser()應該在主線程中被調用,在開始任何線程之前,在程式的入口處.
從原則上如果整個程式中不調用xmlInitParser()在某些情況是可以的,因為上一節提到,調用任何其他libxml2 API時會檢驗是否已經初始化,如果沒有,將自動進行初始化. 但是如果在整個開始線程之前的進程中都沒有調用到libxml2 的任何API,而是線上程開始調用libxml2的API,就會出現初始化時線程競爭的糟糕事情.所以這是非常不推薦的做法.
當然,在進程(線程開始前)多次調用xmlInitParser()不會產生問題,因為第二次以後的調用只是簡單地檢查標誌位接下來返回.
使用xmlCleanupParser()必須遵循一下兩個原則:
(1) xmlCleanupParser()不能線上程中被調用,因為先結束的進程會把共用記憶體清除,接下來尚未結束的的線程就無法正確訪問.
(2) xmlCleanupParser()應該在主線程中被調用,在不再使用libxml2庫時,一般在程式的出口處.
這裡需要注意一個問題,如果你無法確定其他使用者是否還在使用libxml2庫,那麼就不要調用xmlCleanupParser(),因為這樣最差的情況是浪費了一塊記憶體,直至在程式結束時才能被收回,比起程式崩潰,這樣的代價還是值得的.在mailing list中,作者也提到這樣的方案.
同樣,在進程(所有進程結束之後)多次調用xmlCleanupParser()不會對程式產生任何影響,第二次以後的調用僅是檢查標誌位和簡單第返回.
在多線程環境下,推薦的使用方法是:
------------------------------------------------------------------------------
int main ( int argc, char **argv ) {
//do library initialization at the beginning of the program xmlInitParser();
//do other program initialization
…
//start thread
for (i = 0; i < num_threads; i++) { ret = pthread_create … }
//do other program initialization …
//do library cleanup when the program ends up
xmlCleanupParser((); return 0;
}
------------------------------------------------------------------------------
以上是我總結的在單線程和多線程環境下使用xmlInitParser()和xmlCleanupParser()時需要注意的幾點,歡迎大家補充!