以前写的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 ); …… }
好,到此为止咱们就实现了即保证了多线程的安全性,又保证了程序的效率,搞定,收工……