在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(&param, "?");
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(&param, "?");
}

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);
}


TAG:

引用 删除 歌词库   /   2007-08-02 18:52:00

不错,不错`````````````````````

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

日历

« 2008-07-29  
  12345
6789101112
13141516171819
20212223242526
2728293031  

数据统计

  • 访问量: 2127
  • 日志数: 100
  • 建立时间: 2007-01-05
  • 更新时间: 2008-04-07

RSS订阅

Open Toolbar