一、数据类型
在C/C++程序中常存在全局变量、函数内定义的静态变量以及局部变量,对于局部变量来说,其不存在线程安全问题,因此不在本文讨论的范围之内。全局变量和函数内定义的静态变量,是同一进程中各个线程都可以访问的共享变量,因此它们存在多线程读写问题。在一个线程中修改了变量中的内容,其他线程都能感知并且能读取已更改过的内容,这对数据交换来说是非常快捷的,但是由于多线程的存在,对于同一个变量可能存在两个或两个以上的线程同时修改变量所在的内存内容,同时又存在多个线程在变量在修改的时去读取该内存值,如果没有使用相应的同步机制来保护该内存的话,那么所读取到的数据将是不可预知的,甚至可能导致程序崩溃。
如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量,这就需要新的机制来实现,我们称之为Static memory local to a thread (线程局部静态变量),同时也可称之为线程特有数据(TSD: Thread-Specific Data)或者线程局部存储(TLS: Thread-Local Storage)。这一类型的数据,在程序中每个线程都会分别维护一份变量的副本(copy),并且长期存在于该线程中,对此类变量的 *** 作不影响其他线程。如下图:
二、一次性初始化
在讲解线程特有数据之前,先让我们来了解一下一次性初始化。多线程程序有时有这样的需求:不管创建多少个线程,有些数据的初始化只能发生一次。列如:在C++程序中某个类在整个进程的生命周期内只能存在一个实例对象,在多线程的情况下,为了能让该对象能够安全的初始化,一次性初始化机制就显得尤为重要了。——在设计模式中这种实现常常被称之为单例模式(Singleton)。Linux中提供了如下函数来实现一次性初始化:
#include <pthread.h>
// Returns 0 on success, or a positive error number on error
int pthread_once (pthread_once_t *once_control, void (*init) (void))
利用参数once_control的状态,函数pthread_once()可以确保无论有多少个线程调用多少次该函数,也只会执行一次由init所指向的由调用者定义的函数。init所指向的函数没有任何参数,形式如下:
void init (void)
{
// some variables initializtion in here
}
另外,参数once_control必须是pthread_once_t类型变量的指针,指向初始化为PTHRAD_ONCE_INIT的静态变量。在C++0x以后提供了类似功能的函数std::call_once (),用法与该函数类似。使用实例请参考https://github.com/ApusApp/Swift/blob/master/swift/base/singleton.hpp实现。
生产者-消费者模式是一种经典的多线程设计模式。它通常有两类线程,即若干个生产者线程和若干个消费者线程。生产者线程负责提交用户请求,消费者线程负责处理生产者提交的任务。两者线程通过共享内存缓冲区进行通信。
生产者线程将任务提交到共享内存缓冲区,消费者线程并不直接与生产者线程通信,而是在共享内存缓冲区中获取任务,并进行处理。共享内存缓冲区是其核心组件,它负责生产者和消费者之间的通信,避免两种直接通信,生产者和消费者都不需要知道对方的存在。由于共享内存缓冲区的存在,它允许生产者和消费者在执行速度上存在时间差,无论哪一方的速度高于对方,都可以通过共享内存缓冲区得到缓解,保证系统正常运行。
生产者-消费者模式的主要角色及作用:
生产者:用于提交用户请求,提取用户任务,并装入内存缓冲区
消费者:在内存缓冲区中提取处理任务
内存缓冲区:缓存生产者提交的任务或数据,供消费者使用
任务:生产者向内存缓冲区提交的数据结构
Main:使用生产者和消费者的客户端
BlockingQueue充当了共享内存缓存区,用于维护任务或数据队列。PCData对象表示一个生产任务或数据。生产者和消费者对象均引用同一个BlockingQueue对象实例,生产者负责创建PCData对象,并将它加入到BlockingQueue中,消费者则从BlockingQueue中获取PCData。
--参考文献《实战Java高并发程序设计》
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)