C语言的C库提供了策略的更新、加载与同步的方法,这里引入多线程,达到区分读写任务(生产者——消费者 模型)的目的。
示例:
/*@brief check strategy to update, reload, synchronized to read(stoped by SIGINT)@author wen`xuanpei@email 15873152445@163.com(query for any question here)
*/
#include <pthread.h>//pthread_(create|exit), pthread_rwlock_(init|destroy|wrlock|rdlock|unlock),
#include <unistd.h>//sleep,
#include <errno.h>//<cerrno>//errno,
//stat
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>//<csignal>//sig_atomic_t,signal, SIG.+,
#include <stdio.h>//<cstdio>//fopen/fclose,perror,fread/fwrite,size_t,NULL,printf,
#include <stdlib.h>//<cstdlib>//abort,calloc,free,
#include <string.h>//<cstring>//memset,strlen,
#include <time.h>//<ctime>//time_t,/*define and select a strategy file type here*/
#define FORMAT_UNKNOWN (0)
#define FORMAT_XML (1)
#define FORMAT_JSON (2)
#define FORMAT_INI (3)
#if 0
# define STRATEGY_FORMAT FORMAT_XML
#elif 0
# define STRATEGY_FORMAT FORMAT_JSON
#elif 1
# define STRATEGY_FORMAT FORMAT_INI
#else
# define STRATEGY_FORMAT FORMAT_UNKNOWN
#endif
/*auto concate for strategy file path*/
#define concate(x, y) x y//
#if STRATEGY_FORMAT == FORMAT_XML
# define STRATEGY_SUFFIX ".xml"
#elif STRATEGY_FORMAT == FORMAT_JSON
# define STRATEGY_SUFFIX ".json"
#elif STRATEGY_FORMAT == FORMAT_INI
# define STRATEGY_SUFFIX ".ini"
#else//FORMAT_UNKNOWN
# define STRATEGY_SUFFIX ".txt"
#endif
#define STRATEGY_FILE concate("/tmp/strategy", STRATEGY_SUFFIX)
#define STRATEGY_FILE_MAX_SIZE _IO_BUFSIZ
#define thread_failed(s, msg) \
do{ \errno = s, perror(msg); \abort(); \
}while(0)/*to stop the update-thread and main-thread:by user signal SIGINTnotice:to keep data consistence of thread access, don't optmize to cache */
static volatile int stop = 0;
static void handle_exception(sig_atomic_t sig){signal(sig, SIG_IGN);stop = 1;
}
/*exception protection:shadow other signals except SIGINT(SIGKILL,SIGSTOP is special)*/
static void shadow_exception(){sig_atomic_t sig;for(sig = 1; sig <= 64; sig++){if(sig == SIGINT)continue;if(sig > 31 && sig < 34)continue;signal(sig, SIG_IGN);}
}/*to load and update strategy file:by the latest update time(content modification or fresh time only)*/
static time_t latestUpdate;
/*to minimized lock conflict:split reader and writer*/
static pthread_rwlock_t rwLock;
/*to keep integrity of strategy file data:by lock and swap pointer(minimized lock conflict)*/
static char *strategyContent/* = NULL */;
static char *strategyContentBak/* = NULL */;
/*to make more concurrency of reader, copy a strategy self, use it without lock conflict*/
static char *strategyContentCopy/* = NULL*/;//improve performance(for reader)
/*@brief swap for any type of pointer@parama: pointer1(the length is the same as size_t)b: pointer2(the length is the same as size_t)@problem solved(user may be interested in it)convert failed for rvalue(expression) can't be write:(size_t)strategyContent ^= (size_t)strategyContentBakso, write as follows:get address => convert address type => dereference(access momory forcely)*(size_t*)&strategyContent ^= *(size_t*)&strategyContentBak;
*/
#define swap_pointer(a, b) _swap_pointer((size_t*)&a, (size_t*)&b)//wrapper for use it easily!!!
static void _swap_pointer(register size_t *a, register size_t *b){*a ^= *b;*b ^= *a;*a ^= *b;
}
/*swap strategy file buffer*/
static void swap(){int s;if( (s = pthread_rwlock_wrlock(&rwLock)) )thread_failed(s, "pthread_rwlock_wrlock");
#ifndef NDEBUG/*for view of debug*/printf("\nuser update the strategy file now!\n");
#endifswap_pointer(strategyContent, strategyContentBak);pthread_rwlock_unlock(&rwLock);
}
/*reload and update strategy file*/
static void reload(time_t currentUpdate){FILE *fp = NULL;latestUpdate = currentUpdate;if( (fp = fopen(STRATEGY_FILE, "r")) ){memset(strategyContentBak, 0, sizeof(char) * STRATEGY_FILE_MAX_SIZE);//keep clean for textfread(strategyContentBak, sizeof(char), STRATEGY_FILE_MAX_SIZE, fp);fclose(fp), fp = NULL;swap();}else{perror("fopen");abort();}
}
/*update-thread:check if file is freshed, then reloadexcecption protection:after remove strategy file */
static void *update(void* args){struct stat fbuf;time_t currentUpdate = 0;while(!stop){if(-1 == stat(STRATEGY_FILE, &fbuf) ){perror("stat");//to get position, __FILE__,__FUNCTION__,__LINE__ may be usedgoto __next_round;//avoid fresh the screen frequently}currentUpdate = fbuf.st_mtime;if(currentUpdate > latestUpdate)reload(currentUpdate);
__next_round:sleep(4);}
#ifndef NDEBUG/*for view of debug*/printf("\nupdate-thread exit now!\n");
#endifpthread_exit(NULL);
}/*allocate and deallocate for system resource*/
static char hasInit/* = 0*/;
static void init(){int s;pthread_t tid;signal(SIGINT, handle_exception);if( (s = pthread_create(&tid, NULL, update, NULL)))thread_failed(s, "pthread_create");if( (s = pthread_rwlock_init(&rwLock, NULL)) )thread_failed(s, "pthread_rwlock_init");if( !(strategyContent = calloc(sizeof(char), STRATEGY_FILE_MAX_SIZE) ) ){perror("calloc");abort();}if( !(strategyContentBak = calloc(sizeof(char), STRATEGY_FILE_MAX_SIZE) ) ){perror("calloc");abort();}if( !(strategyContentCopy = calloc(sizeof(char), STRATEGY_FILE_MAX_SIZE) ) ){perror("calloc");abort();}hasInit = 1;
}
static void destroy(){if(hasInit){pthread_rwlock_destroy(&rwLock);free(strategyContent), strategyContent = NULL;free(strategyContentBak), strategyContentBak = NULL;free(strategyContentCopy), strategyContentCopy = NULL;}
}/*compatible for multi-thread shared read:read and use the strategy file*/
static int readCount/* = 0 */;
static void read_use(){int s;if( (s = pthread_rwlock_rdlock(&rwLock)) )thread_failed(s, "pthread_rwlock_rdlock");memcpy(strategyContentCopy, strategyContent, STRATEGY_FILE_MAX_SIZE * sizeof(char));pthread_rwlock_unlock(&rwLock);printf("\n>>>%dth read strategyContent:\n", ++readCount);//CLIfwrite(strategyContent, sizeof(char), strlen(strategyContent), stdout);
}/*prepare strategy file for test*/
static void prepare(){
#if STRATEGY_FORMAT == FORMAT_XML//.xmlsystem("echo \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\" >" STRATEGY_FILE ";""echo \"<root>\" >>" STRATEGY_FILE ";""echo \"\t<strategy>\" >>" STRATEGY_FILE ";""echo \"\t\t<person>\" >>" STRATEGY_FILE ";""echo \"\t\t\t<name>john</name>\" >>" STRATEGY_FILE ";""echo \"\t\t\t<age>18</age>\" >>" STRATEGY_FILE ";""echo \"\t\t\t<weight>61.38kg</weight>\" >>" STRATEGY_FILE ";""echo \"\t\t\t<married>false</married>\" >>" STRATEGY_FILE ";""echo \"\t\t</person>\" >>" STRATEGY_FILE ";""echo \"\t</strategy>\" >>" STRATEGY_FILE ";""echo \"</root>\" >>" STRATEGY_FILE );
#elif STRATEGY_FORMAT == FORMAT_JSON//.jsonsystem("echo \"{\" >" STRATEGY_FILE ";""echo \"\t\\\"strategy\\\":{\" >>" STRATEGY_FILE ";""echo \"\t\t\\\"person\\\":{\" >>" STRATEGY_FILE ";""echo \"\t\t\t\\\"name\\\":\\\"john\\\",\" >>" STRATEGY_FILE ";""echo \"\t\t\t\\\"age\\\":\\\"18\\\",\" >>" STRATEGY_FILE ";""echo \"\t\t\t\\\"weight\\\":\\\"61.38kg\\\",\" >>" STRATEGY_FILE ";""echo \"\t\t\t\\\"married\\\":\\\"false\\\"\" >>" STRATEGY_FILE ";""echo \"\t\t}\" >>" STRATEGY_FILE ";""echo \"\t}\" >>" STRATEGY_FILE ";""echo \"}\" >>" STRATEGY_FILE);
#elif STRATEGY_FORMAT == FORMAT_INI//.inisystem("echo \"[strategy]\" >" STRATEGY_FILE ";""echo \"id =1234567890#\" >>" STRATEGY_FILE ";""echo \"name =john#\" >>" STRATEGY_FILE ";""echo \"age =18#\" >>" STRATEGY_FILE ";""echo \"weight =61.38kg#\" >>" STRATEGY_FILE ";""echo \"married=false#\" >>" STRATEGY_FILE);
#else//.txtsystem("touch " STRATEGY_FILE);
#endif
}/*main frame here*/
int main(){shadow_exception();prepare();init();while(!stop){sleep(2);read_use();}
#ifndef NDEBUG/*for view of debug*/printf("\nmain-thread waitting to collect resource ... \n");sleep(10);
#endifdestroy();return 0;
}
小结:
1)可以使用异步信号通知的方式,保持访问标志变量的一致性, 将更新线程与主线程停下来
2)通过最后一次修改时间与上一次做比对,来确定配置文件是否有更新,在同步更新时要注意清理上一次配置文件的内容 并 保持本次配置文件的完整性
3)读时共享,写时互斥(w-w,w-r), 区分writer与reader, 让需要配置的多线程最大程度并发,同时将需要同步竞争的数据拷贝来进一步提高reader并发量
4)更新配置文件一般是比较轻松的任务,可以最大限度让出CPU给系统其它任务使用
提示:实际开发当中配置文件会比较复杂,与业务逻辑强相关,常见的有XML, JSON, INI, TXT需要加载后解析到指定的内存结构,同步分给相应的任务使用