在C++程序中添加逻辑流程控制
上一篇 / 下一篇 2007-01-24 15:36:00 / 个人分类:听说
逻辑流程VS线程
逻辑流程看起来像是虚拟线程,但它实际上运行在创建它的线程空间之内。尽管两者都有独立的进程堆栈,但逻辑流程的开销要小一些,且不用处理流程间的同步问题。
逻辑流程也能用于异常处理。例如,可添加类似如下的代码:
| START_FLOW { FLOW_WAIT(read_err=); … } START_FLOW { FLOW_WAIT(current_tick & last_receive_tick >= RECEIVE_TIMEOUT); … } |
示例对比
下面还有一个例子演示了流程的可伸缩性及威力,比如说要解析以下格式的URL:
| [scheme://[user:pass@]host[:port]]/]uri[?param[#ankor]] |
如果只想遍历URL字符串一次,可能会编写如下代码:
| void URL::ParseString(const string &url) { string s; s.reserve(url.length()); if (Original.empty()) Original = url; OriginalLength = url.length(); const char *p = url.c_str(); //解析scheme [http:] while (*p && (*p != '/') && (*p != ':') && (*p != ';') && (*p != '?') && (*p != '#')) s += *p++; if (*p == ':') { Scheme = s; p++; s.resize(0); while (*p && (*p != '/') && (*p != ';') && (*p != '?') && (*p != '#')) s += *p++; } // 解析 //[user[:pass]@]host[:port]/ // 解析端口) if (*p && (*p == '/') && (*(p+1) == '/')) { p+=2; s.resize(0); while (*p && (*p != '/') && (*p != ':') && (*p != '@')) s += *p++; Host = s; if (*p == ':') { s.resize(0); while (*p && (*p != '/') && (*p != '@')) s += *p++; if (*p != '@') Port = IP_PORT(atol(&s[0])); } if (*p == '@') { p++; if (Host.length() == 0) { User = s; } else { User = Host; Password = s; Host.resize(0); } s.resize(0); while (*p && (*p != '/') && (*p != ':')) s += *p++; Host = s; if (*p == ':') { p++; s.resize(0); while (*p && *p != '/') s += *p++; Port = IP_PORT(atol(&s[0])); } } //重建NetLoc字符串 if (User.length()) { NetLoc = User; if (Password.length()) { NetLoc += ":"; NetLoc += Password; } NetLoc += '@'; } NetLoc += Host; if (Port != 80) { char portstring[15]; NetLoc += ':'; sprintf(portstring, "%d", Port); NetLoc += portstring; } s.resize(0); } //解析路径[/a[/b[..]]/]与文件 //如果遇到'/'且s不为空,这是一个相对路径。 if (s.length() && (*p == '/')) { p++; RelativePath = true; Path.push_back(s); s.resize(0); while (*p && (*p != '/') && (*p != ';') && (*p != '?') && (*p != '#') && (*p != '&')) s += *p++; } else { //这是一个不带反斜线的纯文件名,或者它只是一个主机名。 if (*p != '/') RelativePath = Host.empty(); else { p++; while (*p && (*p != '/') && (*p != ';') && (*p != '?') && (*p != '#') && (*p != '&')) s += *p++; } } //只要当前字后跟有反斜线,就把它追加到路径后。 while (*p == '/') { p++; //if (s.length()) Path.push_back(s); // uri可为'...//...' s.resize(0); while (*p && (*p != '/') && (*p != ';') && (*p != '?') && (*p != '#') && (*p != '&')) s += *p++; } //现在当前字为文件名 File = s; // //获取文件类型 // string::size_type pp = File.rfind('.'); if (pp != string::npos) { FileType = File.substr(pp+1); } //寻找参数 if (*p == ';') { p++; s.resize(0); while (*p && (*p != '?') && (*p != '#') && (*p != '&')) s += *p++; Params = s; } //寻找查询 //接受以'&'打头的查询 if (*p == '?' || *p == '&') { s = *p; //保存前导查询字符 p++; while (*p && (*p != '#')) s += *p++; Query = s; } //寻找片断(fragment) if (*p == '#') { p++; s.resize(0); while (*p) s += *p++; Fragment = s; } } |
如果使用流程的话,代码就会像下面这个样子:
| class Url { string scheme, host, port, user, pass, uri, param, ankor; string* head_token; int last_pos, cur_pos; char* url; parse_url(char* param) { START_FLOW analyze_url(); url = param; int len = strlen(url); last_pos = 0; cur_pos = 0; head_token = NULL; while (cur_pos < len) { cur_pos++; } if (head_token) *head_token = url + last_pos; } void analyze_url() { START_FLOW { read_to_tail(&scheme, "://"); START_FLOW read_from_head(&host, "/"); START_FLOW read_from_head(&port, ":"); START_FLOW { string tmp; read_from_head(&tmp, "@"); user = host; pass = port; host.erase(); port.erase(); read_from_head(&port, ":"); host = tmp; } } START_FLOW { read_from_head(&uri, "/")); START_FLOW read_from_head(¶m, "?"); START_FLOW read_from_head(&anchor, "#"); } } void read_to_tail(string* token, char* end_str) { head_token = token; while (TRUE) { WAIT (cur_pos=); if (memcmp(url + cur_pos, end_str, strlen(end_str)) == 0) break; } head_token->assign(url + last_pos, cur_pos - last_pos); last_pos = cur_pos = cur_pos + strlen(end_str); head_token = NULL; } void read_from_head(string* token, char* start_str) { while (TRUE) { WAIT (cur_pos=); if (memcmp(url + cur_pos, end_str, strlen(end_str)) == 0) break; } if (head_token) head_token->assign(url + last_pos, cur_pos - last_pos); head_token = token; last_pos = cur_pos + 1; } }; |
代码短多了,也易于修改,面对更复杂的格式也更具可伸缩性
使用线程来实现
不幸的是,没有任何编译器可以支持这两个原语,如果想使用它们,只能通过一个线程来实现,虽然会带来一些系统开销, 但是值得。为取代这两个原语,可以使用以下七个宏:
·VFLOW_EVENT_DECLARE(evt):声明一个事件变量。虚拟流程可使用事件来等待或发信号。
·VFLOW_EVENT_INITIALIZE(evt):初始化一个事件变量。这个宏可在C++中并入上一个宏。
·VFLOW_WAIT(evt):一个虚拟流程能调用它来等待一个事件。
·VFLOW_SIGNAL(evt):给一个事件发信号。所有等待事件的虚拟流程将会一个接一个地被激活。当被激活后,将继续之前的流程直至再碰到一个VFLOW_WAIT,此时它又被挂起,而在队列中等待的下一个虚拟流程将会被激活。调用VFLOW_SIGNAL的流程在所有等待的流程全部执行完毕后,才会继续执行。
·VFLOW_TERMINATE(evt):当它被调用时,所有等待事件的虚拟流程会立即退出。
·VFLOW_START(routine, param):要启动一个虚拟流程,需要调用routine(param)。当它遇到第一个VFLOW_WAIT时,它会将执行控制交回它的父流程。
·VFLOW_EXIT:用于虚拟流程的中途退出。
下面是修改后的代码,且在Windows与Linux下都能运行:
| //analyze [scheme://[user:pass@]host[:port]]/]uri[?param[#ankor]] #include "vflow.h" #include <stdio.h> #include <string> using namespace std; class Url; void flow_read_domain(void*); void flow_read_host(void*); void flow_read_port(void*); void flow_read_host_port(void*); void flow_read_query_string(void*); void flow_read_param(void*); void flow_read_anchor(void*); class Url { public: Url() {} ~Url() {} string scheme, host, port, user, pass, uri, param, anchor; string* head_token; int last_pos, cur_pos; char* url; VFLOW_EVENT_DECLARE(cur_pos_changed); void parse_url(char* param) { VFLOW_EVENT_INITIALIZE(cur_pos_changed); url = param; int len = strlen(url); last_pos = 0; set_pos(0); head_token = NULL; analyze_url(); while (cur_pos < len) { set_pos(cur_pos+1); } if (head_token) *head_token = url + last_pos; VFLOW_TERMINATE(cur_pos_changed); uri = "/" + uri; } void set_pos(int pos) { cur_pos = pos; VFLOW_SIGNAL(cur_pos_changed); } void analyze_url() { VFLOW_START(::flow_read_domain, this); VFLOW_START(::flow_read_query_string, this); } void flow_read_domain() { read_to_tail(&scheme, "://"); VFLOW_START(::flow_read_host, this); VFLOW_START(::flow_read_port, this); VFLOW_START(::flow_read_host_port, this); } void flow_read_host() { read_to_tail(&host, "/"); } void flow_read_port() { read_from_head(&port, ":"); } void flow_read_host_port() { string tmp; read_from_head(&tmp, "@"); user = host; pass = port; host.erase(); port.erase(); read_from_head(&port, ":"); host = tmp; } void flow_read_query_string() { read_from_head(&uri, "/"); VFLOW_START(::flow_read_param, this); VFLOW_START(::flow_read_anchor, this); } void flow_read_param() { read_from_head(¶m, "?"); } void flow_read_anchor() { read_from_head(&anchor, "#"); } void read_to_tail(string* token, char* end_str) { head_token = token; while (1) { VFLOW_WAIT(cur_pos_changed); if (memcmp(url + cur_pos, end_str, strlen(end_str)) == 0) break; } head_token->assign(url + last_pos, cur_pos - last_pos); last_pos = cur_pos = cur_pos + strlen(end_str); head_token = NULL; } void read_from_head(string* token, char* start_str) { while (1) { VFLOW_WAIT(cur_pos_changed); if (memcmp(url + cur_pos, start_str, strlen(start_str)) == 0) break; } if (head_token) head_token->assign(url + last_pos, cur_pos - last_pos); head_token = token; last_pos = cur_pos + 1; } }; void flow_read_domain(void* param) { ((Url*)param)->flow_read_domain(); } void flow_read_host(void* param) { ((Url*)param)->flow_read_host(); } void flow_read_port(void* param) { ((Url*)param)->flow_read_port(); } void flow_read_host_port(void* param) { ((Url*)param)->flow_read_host_port(); } void flow_read_query_string(void* param) { ((Url*)param)->flow_read_query_string(); } void flow_read_param(void* param) { ((Url*)param)->flow_read_param(); } void flow_read_anchor(void* param) { ((Url*)param)->flow_read_anchor(); } int main(int argc, char* argv[]) { Url url; url.parse_url("http://user:pass@abc.com:80/abc/def/ ghi.php?jklmn=1234&opq=567#rstuvw"); printf("schema=%s\nuser=%s\npass=%s\nhost=%s\nport=%s\nuri=%s\ nparam=%s\nanchor=%s\n", url.scheme.c_str(), url.user.c_str(), url.pass.c_str(), url.host.c_str(), url.port.c_str(), url.uri.c_str(), url.param.c_str(), url.anchor.c_str()); return 0; } //vflow.h #ifndef _VFLOW_H_ #define _VFLOW_H_ #ifdef WIN32 #include <windows.h> #else #include <pthread.h> #endif #ifdef __cplusplus extern "C" { #endif typedef #ifdef WIN32 DWORD #else pthread_t #endif VF_THREAD_ID; typedef void (*LPVFLOW_START_ROUTINE)(void* param); typedef struct STRU_VIRTUAL_FLOW { VF_THREAD_ID thread_id; struct STRU_VIRTUAL_FLOW* map_prev; struct STRU_VIRTUAL_FLOW* map_next; struct STRU_VIRTUAL_FLOW* evt_next; unsigned short status; // 1 means exit #ifdef WIN32 HANDLE evt; #else pthread_mutex_t mut; pthread_cond_t cond; #endif LPVFLOW_START_ROUTINE routine; void* param; } VIRTUAL_FLOW; typedef struct { VIRTUAL_FLOW* first; VIRTUAL_FLOW* last; } VIRTUAL_FLOW_EVENT; //声明一个流程事件 #define VFLOW_EVENT_DECLARE(evt) \ VIRTUAL_FLOW_EVENT vf_##evt; #define VFLOW_EVENT_INITIALIZE(evt) \ vf_##evt.first = vf_##evt.last = NULL; #define VFLOW_START vf_start //添加到等待队列 #define VFLOW_WAIT(evt) \ vf_wait(&vf_##evt); //给等待事件的流程发信号 #define VFLOW_SIGNAL(evt) \ vf_signal(&vf_##evt); //结束等待某一事件的所有流程 #define VFLOW_TERMINATE(evt) \ vf_terminate(&vf_##evt); #define VFLOW_EXIT vf_exit void vf_start(LPVFLOW_START_ROUTINE routine, void* param); void vf_wait(VIRTUAL_FLOW_EVENT* evt); void vf_signal(VIRTUAL_FLOW_EVENT* evt); void vf_terminate(VIRTUAL_FLOW_EVENT* evt); void vf_exit(); #ifdef __cplusplus } #endif #endif // _VFLOW_H_ //vflow.c #include "vflow.h" #include <stdlib.h> #include <string.h> #ifndef WIN32 #include <sys/types.h> #include <linux/unistd.h> #endif #define VF_MAP_SIZE 17 int g_vf_init = 0; VIRTUAL_FLOW* g_vf_map[VF_MAP_SIZE]; #ifdef WIN32 #define GetThreadId GetCurrentThreadId #else #define GetThreadId pthread_self #endif //基于线程ID,从g_vf_map中获取virtual_flow //如果bCreate = 1,且它不存在,就创建一个。 //否则,如果它存在,从图中删除它。 VIRTUAL_FLOW* get_my_vf(unsigned int bCreate) { VF_THREAD_ID thread_id = GetThreadId(); int n = ((unsigned char)(thread_id >> 24) + (unsigned char)(thread_id >> 16) + (unsigned char)(thread_id >> 8) + (unsigned char)thread_id) / VF_MAP_SIZE; VIRTUAL_FLOW** ppVF = g_vf_map + n; VIRTUAL_FLOW* pVF, *pVF2; if (*ppVF == NULL) { if (!bCreate) return NULL; pVF = (VIRTUAL_FLOW*)malloc(sizeof(VIRTUAL_FLOW)); pVF->map_prev = pVF->map_next = pVF->evt_next = NULL; *ppVF = pVF; } else { pVF = *ppVF; while (1) { if (pVF->thread_id == thread_id) { if (bCreate) return pVF; if (pVF == *ppVF) { *ppVF = pVF->map_next; if (*ppVF) (*ppVF)->map_prev = NULL; } else { pVF->map_prev->map_next = pVF->map_next; if (pVF->map_next) pVF->map_next->map_prev = pVF->map_prev; } #ifdef WIN32 CloseHandle(pVF->evt); #else pthread_cond_destroy(&pVF->cond); #endif free(pVF); return NULL; } if (pVF->map_next == NULL) break; pVF = pVF->map_next; } if (!bCreate) return NULL; pVF2 = (VIRTUAL_FLOW*)malloc(sizeof(VIRTUAL_FLOW)); pVF2->map_prev = pVF; pVF2->map_next = pVF2->evt_next = NULL; pVF->map_next = pVF2; pVF = pVF2; } pVF->thread_id = thread_id; #ifdef WIN32 pVF->evt = CreateEvent(NULL, FALSE, FALSE, NULL); #else pthread_cond_init(&pVF->cond, NULL); pthread_mutex_init(&pVF->mut, NULL); #endif pVF->status = 0; return pVF; } void vf_flow_wait(VIRTUAL_FLOW* vf) { #ifdef WIN32 WaitForSingleObject(vf->evt, INFINITE); #else pthread_cond_wait(&vf->cond, &vf->mut); pthread_mutex_unlock(&vf->mut); #endif if (vf->status > 0) { vf_exit(); #ifdef WIN32 ExitThread(0); #else pthread_exit(NULL); #endif } } void vf_flow_activate(VIRTUAL_FLOW* vf) { #ifdef WIN32 SetEvent(vf->evt); #else pthread_mutex_lock(&vf->mut); pthread_cond_signal(&vf->cond); pthread_mutex_unlock(&vf->mut); #endif } #ifdef WIN32 DWORD WINAPI #else void* #endif vf_flow_routine(void* param) { VIRTUAL_FLOW* parent_vf = (VIRTUAL_FLOW*)param; VIRTUAL_FLOW* vf = get_my_vf(1); vf->evt_next = parent_vf; parent_vf->routine(parent_vf->param); vf_exit(); #ifdef WIN32 return 0; #else return NULL; #endif } void vf_init() { if (g_vf_init) return; memset(g_vf_map, 0, sizeof(g_vf_map)); g_vf_init = 1; } void vf_start(LPVFLOW_START_ROUTINE routine, void* param) { VIRTUAL_FLOW* vf; #ifndef WIN32 pthread_t thread; #endif vf_init(); vf = get_my_vf(1); vf->routine = routine; vf->param = param; #ifdef WIN32 CreateThread(NULL, 0, vf_flow_routine, vf, 0, NULL); #else pthread_mutex_lock(&vf->mut); pthread_create(&thread, NULL, vf_flow_routine, vf); #endif vf_flow_wait(vf); } void vf_wait(VIRTUAL_FLOW_EVENT* evt) { VIRTUAL_FLOW* vf, *vf_next; vf_init(); vf = get_my_vf(1); if (evt->first == NULL) evt->first = evt->last = vf; else { evt->last->evt_next = vf; evt->last = vf; } #ifndef WIN32 pthread_mutex_lock(&vf->mut); #endif vf_next = vf->evt_next; if (vf_next) { vf->evt_next = NULL; vf_flow_activate(vf_next); } vf_flow_wait(vf); } void vf_signal(VIRTUAL_FLOW_EVENT* evt) { VIRTUAL_FLOW* vf, *vf_first; vf_init(); if (!(vf_first = evt->first)) return; vf = get_my_vf(1); #ifndef WIN32 pthread_mutex_lock(&vf->mut); #endif evt->last->evt_next = vf; evt->first = evt->last = NULL; vf_flow_activate(vf_first); vf_flow_wait(vf); } void vf_terminate(VIRTUAL_FLOW_EVENT* evt) { VIRTUAL_FLOW* vf, *vf_first; vf_init(); for (vf = evt->first; vf; vf = vf->evt_next) vf->status = 1; vf_first = evt->first; evt->first = evt->last = NULL; if (vf_first) vf_flow_activate(vf_first); } void vf_exit() { VIRTUAL_FLOW* vf; vf = get_my_vf(1); if (vf->evt_next) vf_flow_activate(vf->evt_next); get_my_vf(0); } |
导入论坛 引用链接 收藏 分享给好友 推荐到圈子 管理 举报
标题搜索
日历
|
|||||||||
| 日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
| 1 | 2 | 3 | 4 | 5 | |||||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 | |||
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | |||
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | |||
| 27 | 28 | 29 | 30 | 31 | |||||
我的存档
数据统计
- 访问量: 2127
- 日志数: 100
- 建立时间: 2007-01-05
- 更新时间: 2008-04-07
