2021SC@SDUSC

目录

一、ZBar中的多线程

线程:cpu调度的最小单位

何为线程安全?

锁机制

二、ZBar中使用多线程的代码示例

Window线程的上锁与解锁

Vedio视频流的上锁与解锁

三、源码分析

使用函数解析——pthread_mutex_init

使用函数解析——pthread_mutex_destroy

互斥锁属性

锁操作

三、总结


一、ZBar中的多线程

在ZBar中,需要进行大量的数据提取和数据获取,若仅仅进行串行处理,往往会出现运行时间过长等问题。因此ZBar中有许许多多的地方都使用了多线程的处理方式,这在前面对各个模块的解析中有所提及。

在引入多线程后,又有许多问题需要注意,例如线程安全的保证、线程同步的实现方式等等。

下面首先对多线程操作时的几个概念以及ZBar中使用多线程的几个地方进行举例说明。

线程:cpu调度的最小单位

线程共享进程的资源,多个线程可以共享同一地址空间和其他资源,比如共享全局变量。线程作为进程的一部分,扮演的角色就是怎么利用中央处理器去运行代码。线程关注的是中央处理器的运行,而不是内存等资源的管理。同一时刻只有一个线程占用cpu,但高速切换给人带来并行的假象。

  • 为什么多线程?

    • 线程比进程更加轻量级,线程更容易、快捷的创建和销毁。
    • 多CPU系统中,使用线程提高CPU利用率。
    • 耗时的操作使用线程,提高应用程序响应。拥有多个线程允许活动彼此重叠进行,从而会加快应用程序执行速度。
    • 并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
    • 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
    • 并行实体共享同一个地址空间和所有可用数据的能力。

何为线程安全?

我们经常会听说某个类是线程安全,某个类不是线程安全的。那么究竟什么叫做线程安全呢?

我们引用《Java Concurrency in Practice》里面的定义:

在不使用额外同步的情况下,多个线程访问一个对象时,不论线程之间如何交替执行或者在调用方进行任何其它的协调操作,调用这个对象的行为都能得到正确的结果,那么这个对象是线程安全的。

也可以这么理解:

多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。

锁机制

通过锁机制,能够保证在多核多线程环境中,在某一个时间点上,只能有一个线程进入临界区代码,从而保证临界区中操作数据的一致性。

所谓的锁,可以理解为内存中的一个整型数,拥有两种状态:空闲状态和上锁状态。加锁时,判断锁是否空闲,如果空闲,修改为上锁状态,返回成功。如果已经上锁,则返回失败。解锁时,则把锁状态修改为空闲状态。


二、ZBar中使用多线程的代码示例

Window线程的上锁与解锁

在渲染窗口时,必须保证渲染函数window.draw是线程安全的。ZBar对于线程安全方面的处理都是采用互斥锁和信号量实现。

static inline int window_lock (zbar_window_t *w)
{int rc = 0;if((rc = _zbar_mutex_lock(&w->imglock))) {err_capture(w, SEV_FATAL, ZBAR_ERR_LOCKING, __func__,"unable to acquire lock");w->err.errnum = rc;return(-1);}return(0);
}static inline int window_unlock (zbar_window_t *w)
{int rc = 0;if((rc = _zbar_mutex_unlock(&w->imglock))) {err_capture(w, SEV_FATAL, ZBAR_ERR_LOCKING, __func__,"unable to release lock");w->err.errnum = rc;return(-1);}return(0);
}

当有渲染窗口的线程获得了互斥锁,其他线程则必须等待,直到其释放锁。

Vedio视频流的上锁与解锁

在对视频流进行锁定和解锁时,都必须保证线程安全,即不允许出现互斥情况,即有一个视频流以上同时进入临界区。

/* video.next_image and video.recycle_image have to be thread safe* wrt/other apis*/
static inline int video_lock (zbar_video_t *vdo)
{int rc = 0;if((rc = _zbar_mutex_lock(&vdo->qlock))) {err_capture(vdo, SEV_FATAL, ZBAR_ERR_LOCKING, __func__,"unable to acquire lock");vdo->err.errnum = rc;return(-1);}return(0);
}static inline int video_unlock (zbar_video_t *vdo)
{int rc = 0;if((rc = _zbar_mutex_unlock(&vdo->qlock))) {err_capture(vdo, SEV_FATAL, ZBAR_ERR_LOCKING, __func__,"unable to release lock");vdo->err.errnum = rc;return(-1);}return(0);
}static inline int video_nq_image (zbar_video_t *vdo,zbar_image_t *img)
{/* maintains queued buffers in order */img->next = NULL;if(vdo->nq_image)vdo->nq_image->next = img;vdo->nq_image = img;if(!vdo->dq_image)vdo->dq_image = img;return(video_unlock(vdo));
}

可以看到,ZBar实现线程安全的方式是使用互斥锁,下面对ZBar互斥锁的实现进行分析。


三、源码分析

从上面的举例中可以看到,ZBar使用互斥锁时使用的核心函数如下:

typedef pthread_mutex_t zbar_mutex_t;static inline int _zbar_mutex_init (zbar_mutex_t *lock)
{
# ifdef DEBUG_LOCKSpthread_mutexattr_t attr;pthread_mutexattr_init(&attr);pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);int rc = pthread_mutex_init(lock, &attr);pthread_mutexattr_destroy(&attr);return(rc);
# elsereturn(pthread_mutex_init(lock, NULL));
# endif
}static inline void _zbar_mutex_destroy (zbar_mutex_t *lock)
{pthread_mutex_destroy(lock);
}static inline int _zbar_mutex_lock (zbar_mutex_t *lock)
{int rc = pthread_mutex_lock(lock);
# ifdef DEBUG_LOCKSassert(!rc);
# endif/* FIXME save system code *//*rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_LOCKING, __func__,"unable to lock processor");*/return(rc);
}static inline int _zbar_mutex_unlock (zbar_mutex_t *lock)
{int rc = pthread_mutex_unlock(lock);
# ifdef DEBUG_LOCKSassert(!rc);
# endif/* FIXME save system code */return(rc);
}

使用函数解析——pthread_mutex_init

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

函数作用:

该函数用于C函数的多线程编程中,互斥锁的初始化。

pthread_mutex_init() 函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。

pthread_mutexattr_init() 函数成功完成之后会返回零,其他任何返回值都表示出现了错误。

函数成功执行后,互斥锁被初始化为未锁住态。

互斥锁pthread_mutex_t的使用:

1. 互斥锁创建

有两种方法创建互斥锁,静态方式和动态方式,ZBar中采用的是后者。

静态初始化互斥锁方法如下:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下:

使用函数解析——pthread_mutex_destroy

static inline void _zbar_mutex_destroy (zbar_mutex_t *lock)
{pthread_mutex_destroy(lock);
}

销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。

互斥锁属性

互斥锁的属性在创建锁的时候指定,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前有四个值可供选择:

* PTHREAD_MUTEX_TIMED_NP

缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

* PTHREAD_MUTEX_RECURSIVE_NP

嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

* PTHREAD_MUTEX_ERRORCHECK_NP

检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。(这正是ZBar使用的互斥锁类型)

pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);

* PTHREAD_MUTEX_ADAPTIVE_NP

适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

锁操作

锁操作主要包括:

int pthread_mutex_lock(pthread_mutex_t *mutex)int pthread_mutex_unlock(pthread_mutex_t *mutex)int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。

对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;

而检错锁则必须由加锁者解锁才有效,否则返回EPERM;

对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。

在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。


三、总结

本次代码分析对ZBar中线程安全的实现方式以及使用函数进行了详细分析,这也是对之前代码分析中一些缺漏的补充说明。

ZBar源码分析——多线程部分代码分析 | 2021SC@SDUSC相关推荐

  1. mybatis源码之执行insert代码分析

    系列文档: mybatis源码之创建SqlSessionFactory代码分析 mybatis源码之创建SqlSessionFactory代码分析 - mapper xml解析 mybatis源码之执 ...

  2. ZBar源码分析(一)

    2021SC@SDUSC 目录 一.zbar.h头文件分析 二.图像处理器代码分析 一.zbar.h头文件分析 在安装路径下,include中有个zbar.h文件,首先从这个头文件入手. zbar.h ...

  3. 手机自动化测试:Appium源码分析之跟踪代码分析四 1

    手机自动化测试:Appium源码分析之跟踪代码分析四 控制器模块 // Appium webserver controller methods // https://github.com/hugs/a ...

  4. ZBar源码分析(五)

    2021SC@SDUSC 目录 一.image头文件分析 二.image源文件分析 1.zbar_image_create函数分析 2._zbar_image_free函数分析 3.一系列get.se ...

  5. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 1...

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionPro ...

  6. 老李推荐:第6章1节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览 1...

    老李推荐:第6章1节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览 在上一章中我们有简要的介绍了事件源是怎么一回事,但是并没有进行详细的描述.那么往下的这几个 ...

  7. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列...

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列 事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些事件 ...

  8. AMCL源码架构讲解与详细分析

    ROS进阶教程(三)AMCL源码分析 AMCL算法简介 AMCL包结构与通信 CmakeLists研究 体系结构与研究 节点文件函数讲解 订阅话题函数 scan_topic initial_pose ...

  9. Unity3D RPG角色扮演游戏源码(上下)-----源代码分析----01-----主角人物动画

    在源代码的里面有fbx格式的模型文件,发现有2个文件,一个是骨骼动画文件,可以分割为多个动画片段,还有一个是模型文件,但是没有动画,但是可以导入分割好的动画片段到动画元素里面, 按照下面的说明,分割了 ...

最新文章

  1. 使用示波器测量扬声器的阻抗实验数据
  2. 【深度学习】基于深度神经网络进行权重剪枝的算法(二)
  3. asp java.class,以下不属于Java程序结构文件的是()。A.asp文件B.java文件C.class文件D.jar文件...
  4. c#重写了窗体的OnKeyDown事件,但是不执行
  5. JS中同名函数有效执行顺序
  6. Windows下误删文件解决办法
  7. Hbase table DDL操作及scala API操作
  8. 自己动手简单实现vbb的URL静态化
  9. JAVA中使用Apache Batik实现SVG文件转PDF文件导出
  10. java验证码实现方式,SpringBoot实现后端验证码,CaptchaUtil美观的后端随机、算术、中文动态验证码
  11. 浏览器扫码打开Android/iOS App
  12. 首届“十大最具价值”智能交互(语音)创业项目遴选榜单丨Xtecher权威发布
  13. 2022年乡村医生考试精选模拟题及答案
  14. 围绕开源的系列思考之二——企业篇
  15. 我的世界回连Center
  16. 利用Excel Power Query获取基金历史净值、估值和日增长率等信息
  17. 第十三届蓝桥杯结束后感悟,也对自己大学写生活的一个简单复盘
  18. 2014年沈航817
  19. skywalking介绍
  20. Neural Collaborative Filtering(NCF) 学习笔记

热门文章

  1. 从神话诗歌到奇幻科学的人类探索史·《月亮》·四
  2. Windows中MPIO配置
  3. 2003-2019年各省市场分割指数全步骤数据+最终结果
  4. JWT详解与基本使用(保姆教程)
  5. java分装_JAVA 分装 - 战狼6的个人空间 - OSCHINA - 中文开源技术交流社区
  6. u码转换 java_把Java中\u格式的unicode编码转成中文
  7. 供应商绩效管理对企业采购组织的重要价值
  8. html prefetch的原理,HTML5 prefetch
  9. 为什么乔布斯最欣赏扎克伯格?
  10. 大众点评超详细爬虫系列3