一、单例模式
1. 概念
- 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
2. 实现思路
- 私有化该类的构造函数,以防止外界创建单例类的对象。
- 使用类的私有静态指针变量指向该类的唯一实例。
- 用一个公有的静态方法获取该实例。
3. 实现方式
- 懒汉模式:在第一次使用该对象时进行初始化。
- 饿汉模式:在程序运行时立即初始化。
4. 实现代码
(1)懒汉模式
# include <memory>
# include <mutex>
using namespace std;
class Single
{
public :
Single ( Single& ) = delete ;
Single& operator = ( const Single& ) = delete ;
static Single* getInstance ( )
{
if ( ptr_ == nullptr )
{
lock_guard< mutex> locker ( mutex_) ;
if ( ptr_ == nullptr )
{
ptr_ = new Single;
}
}
return ptr_;
}
private :
~ Single ( ) { cout << "~Single()" << endl; }
Single ( ) { cout << "Single()" << endl; }
static Single* ptr_;
static mutex mutex_;
} ;
Single* Single:: ptr_ = nullptr ;
mutex Single:: mutex_;
class Single
{
private :
Single ( ) { }
~ Single ( ) { }
public :
static Single* getinstance ( ) ;
} ;
Single* Single :: getinstance ( )
{
static Single obj;
return & obj;
}
(2)饿汉模式
class Single {
private :
Single ( ) { }
~ Single ( ) { }
static Single* p;
public :
static Single* getinstance ( ) ;
} ;
Single* Single:: p = new Single ( ) ;
Single* Single :: getinstance ( ) {
return p;
}
二、生产者-消费者模型
1. 概念
若干个生产者线程,产生任务放入到任务队列中,任务队列满了就阻塞,不满的时候就工作。
若干个消费者线程,将任务从任务队列取出处理,任务队列中有任务就工作,没有任务就阻塞。
生产者和消费者是互斥关系,两者对任务队列访问互斥。
同时生产者和消费者又是一个相互协作与同步的关系,只有生产者生产之后,消费者才能消费。
2. 示例
例:使用条件变量实现生产者和消费者模型,生产者有5个,往链表头部添加节点,消费者也有5个,删除链表头部的节点。
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <pthread.h>
struct Node
{
int number;
struct Node * next;
} ;
struct Node * head = NULL ;
pthread_cond_t cond;
pthread_mutex_t mutex;
void * producer ( void * arg)
{
while ( 1 )
{
pthread_mutex_lock ( & mutex) ;
struct Node * pnew = ( struct Node * ) malloc ( sizeof ( struct Node ) ) ;
pnew-> number = rand ( ) % 1000 ;
pnew-> next = head;
head = pnew;
printf ( "++++++++producer, number = %d, tid = %ld\n" , pnew-> number, pthread_self ( ) ) ;
pthread_mutex_unlock ( & mutex) ;
pthread_cond_broadcast ( & cond) ;
sleep ( rand ( ) % 3 ) ;
}
return NULL ;
}
void * consumer ( void * arg)
{
while ( 1 )
{
pthread_mutex_lock ( & mutex) ;
while ( head == NULL )
{
pthread_cond_wait ( & cond, & mutex) ;
}
struct Node * pnode = head;
printf ( "--------consumer: number: %d, tid = %ld\n" , pnode-> number, pthread_self ( ) ) ;
head = pnode-> next;
free ( pnode) ;
pthread_mutex_unlock ( & mutex) ;
sleep ( rand ( ) % 3 ) ;
}
return NULL ;
}
int main ( )
{
pthread_cond_init ( & cond, NULL ) ;
pthread_mutex_init ( & mutex, NULL ) ;
pthread_t ptid[ 5 ] ;
pthread_t ctid[ 5 ] ;
for ( int i = 0 ; i < 5 ; ++ i)
{
pthread_create ( & ptid[ i] , NULL , producer, NULL ) ;
}
for ( int i = 0 ; i < 5 ; ++ i)
{
pthread_create ( & ctid[ i] , NULL , consumer, NULL ) ;
}
for ( int i = 0 ; i < 5 ; ++ i)
{
pthread_join ( ptid[ i] , NULL ) ;
}
for ( int i = 0 ; i < 5 ; ++ i)
{
pthread_join ( ctid[ i] , NULL ) ;
}
pthread_cond_destroy ( & cond) ;
pthread_mutex_destroy ( & mutex) ;
return 0 ;
}
三、相关函数
1. time()
- 头文件:#include <time.h>
- 函数原型:time_t time(time_t *timer)
- 函数使用:time_t t = time(NULL);
- 函数用途:得到机器的日历时间,单位:秒。
2. localtime()
- 头文件:#include <time.h>
- 函数原型:struct tm *localtime(const time_t *timer)
- 函数使用:struct tm *sys_tm = localtime(&t);
- 函数用途:返回一个以tm结构体表达的机器时间信息。
struct tm
{
int tm_sec; // 秒,取值区间为[0-59]
int tm_min; // 分,取值区间为[0-59]
int tm_hour; // 时,取值区间为[0-23]
int tm_mday; // 日,取值区间为[1,31]
int tm_mon; // 月,取值区间为[0,11]
int tm_year; // 年,其值等于实际年份减去1900
int tm_wday; // 星期,从星期日算起,取值区间为[0-6]
int tm_yday; // 从今年1月1日到目前的天数,取值区间为[0-365]
int tm_isdst; // ?
};
3. gettimeofday()
- 头文件:#include <sys/time.h>
- 函数原型:int gettimeofday(struct timeval*tv, struct timezone *tz);
- 函数使用:gettimeofday(&now, NULL);
- 函数用途:把目前的时间用tv结构体返回,当地时区的信息则放到tz所指的结构体中。
struct timeval
{
long tv_sec; // 秒
long tv_usec; // 微秒
} ;
4. fflush()
- 头文件:#include<stdio.h>
- 函数原型:int fflush(FILE* stream);
- 函数使用:fflush(m_fp);
- 函数用途:用于清空文件缓冲区,如果文件是以写的方式打开的,则把缓冲区内容写入文件。
也用于标准输入(stdin)和标准输出(stdout),用来清空标准输入输出缓冲区。
四、实现代码
1. log.h
# ifndef LOG_H
# define LOG_H
# include <stdio.h>
# include <iostream>
# include <string>
# include <stdarg.h>
# include <pthread.h>
# include "block_queue.h"
using namespace std;
class Log
{
public :
static Log * get_instance ( )
{
static Log instance;
return & instance;
}
static void * flush_log_thread ( void * args)
{
Log :: get_instance ( ) -> async_write_log ( ) ;
}
bool init ( const char * file_name, int close_log, int log_buf_size = 8192 , int split_lines = 5000000 , int max_queue_size = 0 ) ;
void write_log ( int level, const char * format, . . . ) ;
void flush ( void ) ;
private :
Log ( ) ;
virtual ~ Log ( ) ;
void * async_write_log ( )
{
string single_log;
while ( m_log_queue-> pop ( single_log) )
{
m_mutex. lock ( ) ;
fputs ( single_log. c_str ( ) , m_fp) ;
m_mutex. unlock ( ) ;
}
}
private :
char dir_name[ 128 ] ;
char log_name[ 128 ] ;
int m_split_lines;
int m_log_buf_size;
long long m_count;
int m_today;
FILE * m_fp;
char * m_buf;
block_queue< string> * m_log_queue;
bool m_is_async;
locker m_mutex;
int m_close_log;
} ;
# define LOG_DEBUG ( format, . . . ) \
if ( 0 == m_close_log) \
{ \
Log :: get_instance ( ) -> write_log ( 0 , format, ## __VA_ARGS__) ; \
Log :: get_instance ( ) -> flush ( ) ; \
}
# define LOG_INFO ( format, . . . ) \
if ( 0 == m_close_log) \
{ \
Log :: get_instance ( ) -> write_log ( 1 , format, ## __VA_ARGS__) ; \
Log :: get_instance ( ) -> flush ( ) ; \
}
# define LOG_WARN ( format, . . . ) \
if ( 0 == m_close_log) \
{ \
Log :: get_instance ( ) -> write_log ( 2 , format, ## __VA_ARGS__) ; \
Log :: get_instance ( ) -> flush ( ) ; \
}
# define LOG_ERROR ( format, . . . ) \
if ( 0 == m_close_log) \
{ \
Log :: get_instance ( ) -> write_log ( 3 , format, ## __VA_ARGS__) ; \
Log :: get_instance ( ) -> flush ( ) ; \
}
# endif
2. block_queue.h
# ifndef BLOCK_QUEUE_H
# define BLOCK_QUEUE_H
# include <iostream>
# include <stdlib.h>
# include <pthread.h>
# include <sys/time.h>
# include "../lock/locker.h"
using namespace std;
template < class T >
class block_queue
{
public :
block_queue ( int max_size = 1000 )
{
if ( max_size <= 0 )
{
exit ( - 1 ) ;
}
this -> m_max_size = max_size;
this -> m_array = new T[ max_size] ;
this -> m_size = 0 ;
this -> m_front = - 1 ;
this -> m_back = - 1 ;
}
void clear ( )
{
m_mutex. lock ( ) ;
m_size = 0 ;
m_front = - 1 ;
m_back = - 1 ;
m_mutex. unlock ( ) ;
}
~ block_queue ( )
{
m_mutex. lock ( ) ;
if ( m_array != NULL )
delete [ ] m_array;
m_mutex. unlock ( ) ;
}
bool full ( )
{
m_mutex. lock ( ) ;
if ( m_size >= m_max_size)
{
m_mutex. unlock ( ) ;
return true ;
}
m_mutex. unlock ( ) ;
return false ;
}
bool empty ( )
{
m_mutex. lock ( ) ;
if ( 0 == m_size)
{
m_mutex. unlock ( ) ;
return true ;
}
m_mutex. unlock ( ) ;
return false ;
}
bool front ( T & value)
{
m_mutex. lock ( ) ;
if ( 0 == m_size)
{
m_mutex. unlock ( ) ;
return false ;
}
value = m_array[ m_front] ;
m_mutex. unlock ( ) ;
return true ;
}
bool back ( T & value)
{
m_mutex. lock ( ) ;
if ( 0 == m_size)
{
m_mutex. unlock ( ) ;
return false ;
}
value = m_array[ m_back] ;
m_mutex. unlock ( ) ;
return true ;
}
int size ( )
{
int tmp = 0 ;
m_mutex. lock ( ) ;
tmp = m_size;
m_mutex. unlock ( ) ;
return tmp;
}
int max_size ( )
{
return m_max_size;
}
bool push ( const T & item)
{
m_mutex. lock ( ) ;
if ( m_size >= m_max_size)
{
m_cond. broadcast ( ) ;
m_mutex. unlock ( ) ;
return false ;
}
m_back = ( m_back + 1 ) % m_max_size;
m_array[ m_back] = item;
m_size++ ;
m_cond. broadcast ( ) ;
m_mutex. unlock ( ) ;
return true ;
}
bool pop ( T & item)
{
m_mutex. lock ( ) ;
while ( m_size <= 0 )
{
if ( ! m_cond. wait ( m_mutex. get ( ) ) )
{
m_mutex. unlock ( ) ;
return false ;
}
}
m_front = ( m_front + 1 ) % m_max_size;
item = m_array[ m_front] ;
m_size-- ;
m_mutex. unlock ( ) ;
return true ;
}
bool pop ( T & item, int ms_timeout)
{
struct timespec t = { 0 , 0 } ;
struct timeval now = { 0 , 0 } ;
gettimeofday ( & now, NULL ) ;
m_mutex. lock ( ) ;
if ( m_size <= 0 )
{
t. tv_sec = now. tv_sec + ms_timeout / 1000 ;
t. tv_nsec = ( ms_timeout % 1000 ) * 1000 ;
if ( ! m_cond. timewait ( m_mutex. get ( ) , t) )
{
m_mutex. unlock ( ) ;
return false ;
}
}
if ( m_size <= 0 )
{
m_mutex. unlock ( ) ;
return false ;
}
m_front = ( m_front + 1 ) % m_max_size;
item = m_array[ m_front] ;
m_size-- ;
m_mutex. unlock ( ) ;
return true ;
}
private :
locker m_mutex;
cond m_cond;
T * m_array;
int m_size;
int m_max_size;
int m_front;
int m_back;
} ;
# endif
3. log.cpp
# include <string.h>
# include <time.h>
# include <sys/time.h>
# include <stdarg.h>
# include "log.h"
# include <pthread.h>
using namespace std;
Log :: Log ( )
{
m_count = 0 ;
m_is_async = false ;
}
Log :: ~ Log ( )
{
if ( m_fp != NULL )
{
fclose ( m_fp) ;
}
}
bool Log :: init ( const char * file_name, int close_log, int log_buf_size, int split_lines, int max_queue_size)
{
if ( max_queue_size >= 1 )
{
this -> m_is_async = true ;
this -> m_log_queue = new block_queue < string> ( max_queue_size) ;
pthread_t tid;
pthread_create ( & tid, NULL , flush_log_thread, NULL ) ;
}
this -> m_close_log = close_log;
this -> m_log_buf_size = log_buf_size;
this -> m_buf = new char [ this -> m_log_buf_size] ;
memset ( this -> m_buf, '\0' , this -> m_log_buf_size) ;
this -> m_split_lines = split_lines;
time_t t = time ( NULL ) ;
struct tm * sys_tm = localtime ( & t) ;
struct tm my_tm = * sys_tm;
const char * p = strrchr ( file_name, '/' ) ;
char log_full_name[ 256 ] = { 0 } ;
if ( p == NULL )
{
snprintf ( log_full_name, 255 , "%d_%02d_%02d_%s" ,
my_tm. tm_year + 1900 , my_tm. tm_mon + 1 , my_tm. tm_mday, file_name) ;
}
else
{
strcpy ( this -> log_name, p + 1 ) ;
strncpy ( this -> dir_name, file_name, p - file_name + 1 ) ;
snprintf ( log_full_name, 255 , "%s%d_%02d_%02d_%s" ,
dir_name, my_tm. tm_year + 1900 , my_tm. tm_mon + 1 , my_tm. tm_mday, this -> log_name) ;
}
this -> m_today = my_tm. tm_mday;
this -> m_fp = fopen ( log_full_name, "a" ) ;
if ( this -> m_fp == NULL )
{
return false ;
}
return true ;
}
void Log :: write_log ( int level, const char * format, . . . )
{
struct timeval now = { 0 , 0 } ;
gettimeofday ( & now, NULL ) ;
time_t t = now. tv_sec;
struct tm * sys_tm = localtime ( & t) ;
struct tm my_tm = * sys_tm;
char s[ 16 ] = { 0 } ;
switch ( level)
{
case 0 :
strcpy ( s, "[debug]:" ) ;
break ;
case 1 :
strcpy ( s, "[info]:" ) ;
break ;
case 2 :
strcpy ( s, "[warn]:" ) ;
break ;
case 3 :
strcpy ( s, "[erro]:" ) ;
break ;
default :
strcpy ( s, "[info]:" ) ;
break ;
}
this -> m_mutex. lock ( ) ;
this -> m_count++ ;
if ( this -> m_today != my_tm. tm_mday || this -> m_count % this -> m_split_lines == 0 )
{
char new_log[ 256 ] = { 0 } ;
fflush ( this -> m_fp) ;
fclose ( this -> m_fp) ;
char tail[ 16 ] = { 0 } ;
snprintf ( tail, 16 , "%d_%02d_%02d_" , my_tm. tm_year + 1900 , my_tm. tm_mon + 1 , my_tm. tm_mday) ;
if ( this -> m_today != my_tm. tm_mday)
{
snprintf ( new_log, 255 , "%s%s%s" , this -> dir_name, tail, this -> log_name) ;
this -> m_today = my_tm. tm_mday;
this -> m_count = 0 ;
}
else
{
snprintf ( new_log, 255 , "%s%s%s.%lld" , this -> dir_name, tail, this -> log_name, this -> m_count / this -> m_split_lines) ;
}
this -> m_fp = fopen ( new_log, "a" ) ;
}
this -> m_mutex. unlock ( ) ;
va_list valst;
va_start ( valst, format) ;
string log_str;
m_mutex. lock ( ) ;
int n = snprintf ( this -> m_buf, 48 , "%d-%02d-%02d %02d:%02d:%02d.%06ld %s " ,
my_tm. tm_year + 1900 , my_tm. tm_mon + 1 , my_tm. tm_mday,
my_tm. tm_hour, my_tm. tm_min, my_tm. tm_sec, now. tv_usec, s) ;
int m = vsnprintf ( this -> m_buf + n, this -> m_log_buf_size - 1 , format, valst) ;
this -> m_buf[ n + m] = '\n' ;
this -> m_buf[ n + m + 1 ] = '\0' ;
log_str = this -> m_buf;
this -> m_mutex. unlock ( ) ;
if ( m_is_async && ! m_log_queue-> full ( ) )
{
this -> m_log_queue-> push ( log_str) ;
}
else
{
m_mutex. lock ( ) ;
fputs ( log_str. c_str ( ) , m_fp) ;
m_mutex. unlock ( ) ;
}
va_end ( valst) ;
}
void Log :: flush ( void )
{
m_mutex. lock ( ) ;
fflush ( m_fp) ;
m_mutex. unlock ( ) ;
}