以前寫的LIBPNG庫學習小結介紹了怎麼樣自定義LIBPNG庫的write、read、flush函數,而不使用LIBPNG庫提供的默認函數。安全
上一篇講述的都是在單線程的狀況下,今天將程序升級,放在多線程下面跑,發現了幾個問題:
首先說明一下:上一篇中是用struct保存的數據結構,而此次須要將數據封裝在類中,所以程序有點小變更。如下是類的部分定義:數據結構
private: png_infop m_pInfo; png_structp m_pPng; char *m_pImage;
其中m_pImage就是上一篇tData中的data。多線程
上一篇中tData是全局定義的,在多線程的狀況下會發生爭搶資源的狀況,可查看上一篇中的代碼,在void PNGAPI png_own_write_data(png_structp png_ptr, png_bytep data, png_size_t length)函數中須要一個變量來存儲每次寫入的位置,不能每次都從第0個位置開始寫吧?會爭搶的資源就是這個記錄位置的變量。這該怎麼解決呢?函數
1-首先想到的辦法就是將全部數據成員和函數成員做爲類的私有成員訪問便可解決問題,但是編譯的時候卻提示png_own_write_data()和png_own_flush()函數出錯,具體錯誤信息就不寫了,此路不通。學習
2.-加鎖能夠解決這個問題,可是加鎖效率很低,怎麼辦呢?有沒更好的辦法?spa
想到的解決辦法:線程
1-在png_struct中找一個閒置的字段來存儲這個變量,但是我看完了png_struct的定義,也不肯定那個變量是沒有用的,雖然有不少字段的值從始至終都是0,可是我仍是不敢貿然使用,由於不知道何時這個值就被使用了。指針
2-仍是在png_struct中找一個閒置的字段,不過此次不是使用某個int或long型的變量了,而是使用函數指針,沒錯,就是 函數指針,由於任何指針在系統中都是一個內存地址(4字節),能夠當作一個int類型來使用。code
下面就詳細的介紹一下這種方法:對象
1-首先鎖定的是output_flush_fn,在上一篇中也說了,默認狀況下
PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
這個宏是沒有被定義的,所以output_flush_fn()這個函數就一直不會執行,因此output_flush_fn這個函數指針是一直沒有使用的,可是若使用本身編譯的LIBPNG庫,而剛好定義了上面的那個宏,那麼這樣程序就會發生意想不到的錯誤。所以此方法能夠用,可是要慎重。
2-根據read和write的互斥性,咱們能夠交錯的使用write_data_fn和read_data_fn這兩個函數指針來保存這個偏移地址。由於咱們通常不會在讀的時候進行寫,也不會在寫的時候進行讀操做(要是有這種狀況,你仍是老老實實的使用output_flush_fn吧,這種狀況不適合你),因此咱們在寫的時候使用read_data_fn來保存寫偏移地址,在讀的時候使用write_data_fn來保存讀偏移地址,具體請見代碼:
void PNGAPI pngOwnReadData( png_structp pPng, png_bytep data, png_size_t length ) { png_bytep src = (png_bytep)pPng->io_ptr; //強制類型轉換,得到偏移地址 int offSet = (int)pPng->write_data_fn; memcpy( data, src + offSet, length ); offSet += length; //將偏移地址強轉成png_rw_ptr類型,該類型是png庫定義的函數指針類型 pPng->write_data_fn = (png_rw_ptr)offSet; }
而在讀取的主函數裏面必須有這樣的代碼:
void readPNG(…… ) { png_voidp io_ptr = (void *)image; //將write_data_fn初始化爲0 m_pPng->write_data_fn = (png_rw_ptr)0; png_set_read_fn( m_pPng, io_ptr, pngOwnReadData ); png_read_png( m_pPng, m_pInfo, PNG_TRANSFORM_EXPAND, NULL ); }
一樣的寫函數也具備一樣的結構:
void PNGAPI pngOwnWriteData(png_structp pPng, png_bytep data, png_size_t length) { …… png_bytep src = (png_bytep)pPng->io_ptr; int offSet = ( int )pPng->read_data_fn; memcpy( src + offSet, data, length ); offSet += length; pPng->read_data_fn = (png_rw_ptr)offSet; …… }
char* writePNG(…… ) { …… png_voidp io_ptr = (void *)m_pImage; m_pPng->read_data_fn = (png_rw_ptr)0; png_set_IHDR( m_pPng, m_pInfo, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_set_rows( m_pPng, m_pInfo, rgb ); png_set_write_fn( m_pPng, io_ptr, pngOwnWriteData, NULL ); png_write_png( m_pPng, m_pInfo, PNG_TRANSFORM_EXPAND, NULL ); //在對象銷燬前獲取長度信息 length = (int)m_pPng->read_data_fn; png_destroy_write_struct( &m_pPng, &m_pInfo ); …… }
好,到此爲止咱們就實現了即保證了多線程的安全性,又保證了程序的效率,搞定,收工……