之前写的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.-加锁可以解决这个问题,但是加锁效率很低,怎么办呢?有没更好的办法?

想到的解决办法:

1-在png_struct中找一个闲置的字段来存储这个变量,可是我看完了png_struct的定义,也不确定那个变量是没有用的,虽然有很多字段的值从始至终都是0,但是我还是不敢贸然使用,因为不知道什么时候这个值就被使用了。

2-还是在png_struct中找一个闲置的字段,不过这次不是使用某个int或long型的变量了,而是使用函数指针,没错,就是   函数指针,因为任何指针在系统中都是一个内存地址(4字节),可以当做一个int类型来使用。

下面就详细的介绍一下这种方法:

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);
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);
png_set_IHDR( m_pPng, m_pInfo, width, height, , 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 );
……
}

好,到此为止我们就实现了即保证了多线程的安全性,又保证了程序的效率,搞定,收工……

LIBPNG使用小结(二)的更多相关文章

  1. gulp使用小结(二)

    接上篇文章接Gulp使用小结(一) 内容如下: 首先,偶在gulp-demos上已经提交了个较通用的栗子...俺琢磨半天,原准备分阶段搞些 Gulp 套路,但是写完介个栗子之后,觉得已经能覆盖绝大多数 ...

  2. Vue学习小结(二)

    接上一批,小结(二). 三.导航内容(含左侧导航及顶部面包屑导航) 其实导航条主要根据element-ui的教程进行编写,官网:http://element-ui.cn/#/zh-CN/compone ...

  3. python --- 字符编码学习小结(二)

    距离上一篇的python --- 字符编码学习小结(一)已经过去2年了,2年的时间里,确实也遇到了各种各样的字符编码问题,也能解决,但是每次都是把所有的方法都试一遍,然后终于正常.这种方法显然是不科学 ...

  4. Element Vue 开箱即用框架如何使用-测试开发【提测平台】阶段小结(二)

    微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. 上一篇总结了后端服务接口的开发,这篇我们主要来总结下前后端分离开发中的前端部分,主要是开箱即用的框架介绍和之前章节组件的梳理和部分的扩展内 ...

  5. Spring知识点小结(二)

    一.配置非自定义的Bean(数据源DataSource模型) DBCP数据源:        导入dbcp的jar包:dbcp+pool+connector                代码实现:  ...

  6. 动态规划小结 - 二维动态规划 - 时间复杂度 O(n*n)的棋盘型,题 [LeetCode] Minimum Path Sum,Unique Paths II,Edit Distance

    引言 二维动态规划中最常见的是棋盘型二维动态规划. 即 func(i, j) 往往只和 func(i-1, j-1), func(i-1, j) 以及 func(i, j-1) 有关 这种情况下,时间 ...

  7. Hibernate知识点小结(二)

    一.持久化对象和标识符    1.持久化类        配置完关系后,操作的实体对应的类,成为持久化类 (Customer) 2.持久化类标识符(oid:object id)        3.持久 ...

  8. Struts2知识点小结(二)

    一.结果视图的配置    <result name="success">/success.jsp</result>        1.局部结果视图      ...

  9. Java并发包中常用类小结(二)

    6.ThredPoolExecutor ThredPoolExecutor是基于命令模式下的一个典型的线程池的实现,主要通过一些策略实现一个典型的线程池,目前已知的策略有ThreadPoolExecu ...

随机推荐

  1. LightOJ 1220 Mysterious Bacteria 水题

    暴力就行了,找出素因子,正的最多是30,然后负的最多是31(这一点wa了一次) #include <cstdio> #include <iostream> #include & ...

  2. [Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6266442.html 在Dagger 2中Activities ...

  3. 【原】Spark数据本地性

    Spark数据本地性 分布式计算系统的精粹在于移动计算而非移动数据,但是在实际的计算过程中,总存在着移动数据的情况,除非是在集群的所有节点上都保存数据的副本.移动数据,将数据从一个节点移动到另一个节点 ...

  4. hotplug\uevent机制(1)

    hotplug就是热拔插,在linux里面,这个功能是通过class_device_create这个函数来实现的,那么我们来分析下这个函数: class_device_create(cls, NULL ...

  5. ZOJ-3721 Final Exam Arrangement 贪心

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3721 容易的贪心题,排个序.. //STATUS:C++_AC_ ...

  6. UVa1328 - Period(KMP找最短循环节)

    题目大意 给定一个长度为n的字符串,求它的每个前缀的最短循环节 题解 白书例题~~~ "错位部分"长度为i-f[i], 如果这个前i个字符能够组成一个周期串,那么"错位& ...

  7. 简述configure、pkg-config、pkg_config_path三者的关系

    简述configure.pkg-config.pkg_config_path三者的关系 一.什么是configure 源码安装过程中大多会用到configure这个程序,一般的configure都是一 ...

  8. javascript function对象

    <html> <body> <script type="text/javascript"> Function.prototype.get_my_ ...

  9. hdoj 5112 A Curious Matt

    A Curious Matt Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/Others) ...

  10. C++:private继承与public继承

    1 private, public, protected 访问标号的访问范围 private:只能由1.该类中的函数.2.其友元函数访问. 不能被任何其他访问,该类的对象也不能访问. protecte ...