一动不如一静

C++博客 首页 新随笔 联系 聚合 管理
  20 Posts :: 0 Stories :: 10 Comments :: 0 Trackbacks

#

前段时间用了ACE_TP_Reactor做了一个东西,但是对这块东西不是很有底,所以借着假期仔细的看了一下这一块的东西,又上网查了一下相关的资料。

在Addison-Wesley - C++NetworkProgrammingVol2的4.3 The ACE_TP_Reactor Class中有这样几句话,让我足足想了一天。

1.Multiple threads running an ACE_TP_Reactor event loop can process events concurrently on different handles
2.They can also dispatch timeout and I/O callback methods concurrently on the same event handler
3.The only serialization in the ACE_TP_Reactor occurs when I/O events occur concurrently on the same handle
4.After a thread obtains a set of active handles from select(), the other reactor threads dispatch from that handle set instead of calling select() again

其实上面的3句话主要表达这样的几层含义
1.并行处理不同handle上的事件
2.并行处理同一event handler上的超时回调函数和I/O回调函数
3.多个线程串行处理同一个handle上的I/O事件。
4.虽然TP_Reactor是用leader/follow模式轮流调用select,但是如果一个select()获得了多个激活的handle,那么其他的线程会分发这些handle ,而不是去再次调用select.
 (这点还没有想通,也没有看见是如何实现的?)

“多个线程串行处理同一个handle上的I/O事件”
       这个是如何达到的呢?ACE源码中,当处理I/O事件的时候,会将HANDLE挂起,使得不再对该HANDLE做事件侦听。来达到同一个handle上的I/O事件是被多个线程串行地处理。

"并行处理同一event handler上的超时回掉函数和I/O回调函数"
       这样好像就比较麻烦了。因为这就意味着TP_Reactor只保证同一个handle下不会有多线程同时调用I/O事件,但是却有可能同时调用超时回调函数和I/O回调函数。如果在这两个函数中有对数据的访问和操作,这就意味着需要有锁的引入。例外,如果在定时器处理中,超过定时的事件间隔,就会有令一个线程再次调用定时器的处理函数,一下子引入了很多同步的问题。如何解决这个问题呢?

方法一:

            更改ACE的源码,象处理socket事件一样,在处理定时事件的时候,也把HANDLE挂起。来自http://cpunion.cnblogs.com/archive/2005/08/09/210941.html

int
ACE_TP_Reactor::handle_timer_events (
int & /*event_count*/,
                                     ACE_TP_Token_Guard 
&guard)
{
  
// Get the current time
  ACE_Time_Value cur_time (this->timer_queue_->gettimeofday () +
                           
this->timer_queue_->timer_skew ());

  
// Look for a node in the timer queue whose timer <= the present
  
// time.
  ACE_Timer_Node_Dispatch_Info info;

  
if (this->timer_queue_->dispatch_info (cur_time,
                                         info))
    {
        
// ******** fixed by lijie ***********
        if (info.type_->get_handle () != ACE_INVALID_HANDLE)
        {
            
if (this->is_suspended_i (info.type_->get_handle ()))
                
return 0;

            
this->suspend_handler (info.type_->get_handle ());
        }
        
// ******** end **********************

      
const void *upcall_act = 0;

      
// Preinvoke.
      this->timer_queue_->preinvoke (info,
                                     cur_time,
                                     upcall_act);

      

      
// Release the token before dispatching notifies
      guard.release_token ();

      
// call the functor
      this->timer_queue_->upcall (info,
                                  cur_time);

      
// Postinvoke
      this->timer_queue_->postinvoke (info,
                                      cur_time,
                                      upcall_act);

      
// We have dispatched a timer
      return 1;
    }

  
return 0;
}

handle_timer处理完以后,返回以前,加上这句话
this->reactor ()->resume_handler (this->get_handle ());

当然别忘了为Handler编写get_handle()函数:
ACE_HANDLE Test_Handler::get_handle () const
{
    
return this->peer ().get_handle ();
}


方法二:
             利用ACE_PipeACE_Message_Queue把所有的事件都排队到同一个I/O HANDLE上去,再由ACE_TP_Reactor通过多个线程顺序串行地触发我们旧的event_handler来处理这些已经排好队的事件/消息。我比较赞成用这样方法。该方法来自:http://blog.csdn.net/zhdaniel/archive/2006/06/29/850888.aspx
   

      


方法三:
             ^_^干脆就不要对同一个event  handler注册I/O事件和其他事件。





posted @ 2006-10-07 11:29 一动不如一静 阅读(3048) | 评论 (1)编辑 收藏

来源:
http://www.ociweb.com/products/mpc/faq.html#006

Q :当生成工程文件名称的时候,可以改变工程文件的名称吗?
A:当用MPC生成工程文件的时候,可以用-name_modifier 选项来修改工程文件名称.如果你也想修改工程的名称,可以增加-apply_project.如下:
mwc.pl -static -type vc6 -name_modifier *_Static -apply_project
如果用了这些选项,所有的工作空间,工程文件和工程名都以_Static结尾.

Q:怎样用MPC在每个目录生成一个工作空间?
A:当运行mwc.pl的时候加上-hierarchy选项.对于所有基于'make'的工作空间是默认的.

Q:怎样告诉MPC在寻找mwc和mpc文件时,不要寻找某个特殊的路径?
A:-exclude可以用于这个目的.但是这个路径必须是一个相对于MPC运行的路径.如:
mwc.pl -exclude this_dir,this_dir_too,and_another_dir

Q:怎样在我的工作空间中用-value_template
A:在mwc文件中,你能用很多mwc.pl的命令行选项.要加上cmdline关键字.如果在命令行中需要有空格,必须包含在""之中.如:
workspace {
  // Set the command line value to change the configurations with vc6, vc71 and vc8
  cmdline += -value_template "configurations='MFC Release' 'MFC Debug'"
}

Q:有没有办法在Solaris,工程中包含一个文件,但是在Linux上不包含该文件呢?
A:你可以用feature定义一个base工程.如下:
// config/default.features
solaris_only = 0


// some_base.mpb
feature(solaris_only) {
  Source_Files {
    source1.cpp
    source2.cpp
  }



// your_project.mpc
project: some_base {
  Source_Files {
    source3.cpp
    source4.cpp
  }


Q:可不可以一些选项用于某客户文件,而另外一些用于其他的客户文件呢?
A:你可以为用户的输入文件创建多个部分.如下:
project: foo {
FOO_Files {
foo.file
}

FOO_Files {
commandflags += -QR
bar.file
}
}
Q:有没有办法表明编译选项?
A:可以用'specific' .如:
project {

  specific(make) {
    compile_flags += -fcheck-new
  }

  specific(vc71) {
    compile_flags += /wd4103
  }


Q:在一个工作空间中,想有静态和动态的工程?
A:在mwc文件中,建立一个域范围.增加-static到命令行选项.如:
workspace {
  // These projects will be static
  some_scope_name {
    cmdline += -static
    my_projects_dir  
    my_other_projects_dir
  }

  // Anything outside the scope will have the command line options provided at the start of mwc.pl
  another_dir
}

Q:有没有方法在mpc文件中表明这个库必须是静态库,其他的库不能把它用做动态库?
A:设置静态库名,而动态库名为空.
project {
  sharedname =
  staticname = foo
}

Q:MPC添加d.lib到所有的库.有没有办法关掉它?
A:在mpc文件中用'lit_libs'代替libs

Q:为vc6和vc71生成静态工程,可是却没有工程的依赖?
A:可以通过设置MPC_DEPENDENCY_COMBINED_STATIC_LIBRARY环境变量,强迫MPC生成工程间相互依赖的的静态库.

Q:有没有方法可以强制在静态库名后有's'但是debug版本没有'd'
A:是的,你能设置lib_modifier,用这个配置座位域修改器.在下面的例子中,假设-static选项已经传递给了MPC
project {
  // Override the values of 's' and 'sd' respectively.
  specific {
    Release::lib_modifier =
    Debug::lib_modifier = d
  }


Q:由MPC生成的VC71工程,用release ,生成的执行文件在release目录中.而debug是在"."目录中,怎样才能把执行文件放在debug目录中.    
A: 增加改行到MPC工程中
project {                   
  specific(nmake, vc6, vc7, vc71, vc8) {
    Debug::install = Debug
  }
}

Q:如何生成支持MFC的版本
A:MPC的命令行增加下面一行
-value_template "configurations='MFC Release' 'MFC Debug'"
这个可以用于 em3, nmake, vc6, vc7, vc71 和 vc8 工程类型的配置
提醒一下,别忘了创建config.h文件,内容类似如下:
#include "config-win32.h"
#include "config-win32-msvc-7.h"



posted @ 2006-06-24 10:18 一动不如一静 阅读(1327) | 评论 (1)编辑 收藏

       ACE_WFMO_Reactor允许多个线程并发的调用handle_events(),这个能力的增加使得它的设计变的复杂,并且引入了一些同基于select的反应器不同的地方.不同点如下:
  • 注册改变的协调.已注册句柄集合的每一个改变,将会影响到所有执行事件循环的线程.在没有同步的情况下允许这些改变的发生,会导致丢失事件,或不正确,不合法的句柄被分发.为了在多线程的情况下,正确的处理注册发生改变的情况,ACE_WFMO_Reactor维持了三个句柄信息集合对象.
  1. 用作事件侦查和分发的当前句柄
  2. 新句柄, 除了当前句柄集合,新句柄也在等待
  3. 挂起句柄,是当前句柄中挂起的句柄
当需要改变已注册的句柄时,(例如:注册,移除,挂起和恢复一个事件处理器),要记住句柄,事件处理器,事件类型信息,和相关信息的记录.下一个完成handle_events的线程会注意到这个改变,获取reactor的锁,等待所有其他正在运行handle_events的线程完成.为了能让这些线程及时的完成,等待的线程发送一个内部事件,该内部事件是分发句柄集合的一部分.唤醒所有阻塞在WaitForMultipleObjects的线程.这个时候,所有的事件处理线程将会阻塞在等待变化的发生.当原始的线程完成必要的信息和句柄改变后,释放reactor的锁.所有的事件处理线程重新开始在更新后的句柄集合上进行等待,分离和分发.
  • 推迟事件处理器的清除.ACE_WFMO_Reactor注册改变的延迟引入了一些与基于select()的反应器的不同之处.当hanle_*()方法返回-1或者ACE_Reactor::remove_handler()被事件处理器调用时,它推迟事件处理器的移除和handle_close()钩子函数的回调,直到上面说讲的注册改变发生.所以一个应用在请求ACE_WFMO_Reactor移除一个事件处理器之后,不能立即删除该事件处理器,否则reator后来调用的handle_close()方法会分发一个无效的指针.
posted @ 2006-06-23 20:48 一动不如一静 阅读(784) | 评论 (0)编辑 收藏

1.修饰类的数据成员
const数据成员只是在某个对象的生命周期内是常量.如果创建多个对象,那么每个对象的const数据成员可以不一样.所以,不能在类声明中初始化const数据成员.如:
class A
{
   const int size = 100;// 错误
   int array[size];//错误,未知的数组大小
}
const类数据成员,只能在类的构造函数的初始化列表中进行.要想建立整个类都是恒定的常量的数据,可以用类中的枚举.如:
class A
{
enum {size1 = 100, size2 = 200};
int array[size1];
}

2.只有声明为const的成员函数才能被一个const对象调用

3.在另一连接文件中引用const常量.
extern const int i

4.对于const对象,c++既允许对其进行静态初始化,也允许对其进行动态初始化.const对象具有从构造函数完成到析构函数执行之前的不变性.

5.常量与数组的组合有什么特殊吗?
我们给出下面的代码:
const int size[3]={10,20,50};
int array[size[2]];
编译通不过!为什么呢?const可以用于集合,但编译器不能把一个集合存放在它的符号表里,所以必须分配内存。在这种情况下,const意味着“不能改变的一块存储”。然而,其值在编译时不能被使用,因为编译器在编译时不需要知道存储的内容。自然,作为数组的大小就不行了
你再看看下面的例子:
class A
{
public:
A(int i=0):test[2]({1,2}) {} file://你/认为行吗?
private:
const int test[2];
};
C++标准有一个规定,不允许无序对象在类内部初始化,数组显然是一个无序的,所以这样的初始化是错误的!对于他,只能在类的外部进行初始化,如果想让它通过,只需要声明为静态的,然后初始化。
这里我们看到,常量与数组的组合没有什么特殊!一切都是数组惹的祸!

(7)什么情况下为const分配内存?
以下是我想到的可能情况,当然,有的编译器进行了优化,可能不分配内存。
A、作为非静态的类成员时;
B、用于集合时;
C、被取地址时;
D、在main函数体内部通过函数来获得值时;
E、const的 class或struct有用户定义的构造函数、析构函数或基类时;。
F、当const的长度比计算机字长还长时;
G、参数中的const;
H、使用了extern时。

(8)与static搭配会不会有问题?
假设有一个类:
class A
{
public:
......
static void f() const { ......}
......
};
我们发现编译器会报错,因为在这种情况下static不能够与const共存!因为static没有this指针,但是const修饰this指针.

(9)如何修改常量?
有时候我们却不得不对类内的数据进行修改,但是我们的接口却被声明了const,那该怎么处理呢?我对这个问题的看法如下:
1)标准用法:mutable
class A
{
public:
A(int i=0):test(i) { }
void Setvalue(int i)const { test=i; }
private:
mutable int test; file://这/里处理!
};
2)强制转换:const_cast
class A
{
public:
A(int i=0):test(i) { }
void Setvalue(int i)const
{ const_cast <int>(test)=i; }//这里处理!
private:
int test;
};

(10)最后我们来讨论一下常量对象的动态创建。
既然编译器可以动态初始化常量,就自然可以动态创建,例如:
const int* pi=new const int(10);
这里要注意2点:
1)const对象必须被初始化!所以(10)是不能够少的。
2)new返回的指针必须是const类型的。
那么我们可不可以动态创建一个数组呢?
答案是否定的,因为new内置类型的数组,不能被初始化。
posted @ 2006-06-14 17:49 一动不如一静 阅读(411) | 评论 (0)编辑 收藏

c++中的引用又称为别名.

1. 引用必须被初始化,即必须指向一个对象.
 int nTemp = 10;
int &refTemp = nTemp;

2.初始化之后就不能再指向其他对象.如果用一个引用给另一个引用赋值,那么被改变的是被引用的对象而不是引用本身.
int nOther = 20;
refTemp = nOther; //此时nTemp中的值为20,但是&refTemp和&nTemp是相同的.

3.用const修饰的引用可以用不同类型的对象初始化(只要能从一种类型转换到另外一种类型),也可以是不可寻址的值.能够这样的原因是因为:编译器做了手脚.
double dTemp = 1024;
const int &refTemp = dTemp; //&dTemp和&refTemp是不同的.

编译之后成为:
double dTemp = 1024;
int nTemp = dTemp;
const int &refTemp = nTemp;

4.不允许非const引用指向需要临时对象的对象或值.


posted @ 2006-06-14 15:55 一动不如一静 阅读(434) | 评论 (0)编辑 收藏

两本书的错误的更新
http://www.cs.wustl.edu/~schmidt/ACE/book1/Errata.txt

http://www.cs.wustl.edu/~schmidt/ACE/book2/Errata.txt

英文讨论组
http://groups.google.com/group/comp.soft-sys.ace

http://groups.google.com/group/comp.soft-sys.ace?lnk=oa

中文讨论区
http://forum.huihoo.com/forumdisplay.php?fid=125

相关文档
http://www.cs.wustl.edu/~schmidt/ACE.html

有关mpc问题
http://www.ociweb.com/products/mpc/faq.html#021

源码中标记的含义
http://www.stack.nl/~dimitri/doxygen/commands.html
posted @ 2006-06-13 15:23 一动不如一静 阅读(612) | 评论 (0)编辑 收藏

         #include <iostream>
         #include <string>

         std::cin.widen(1024);

         for (; ;)
         {
               std::string user_input;
               std::getline(std::cin, user_input, '\n');
              
               if  (!std::cin || std::cin.eof()) break;
             
               std::cerr << user_input;
         
         }

posted @ 2006-06-13 10:01 一动不如一静 阅读(283) | 评论 (0)编辑 收藏

这篇内容来自c++网络编程卷1 第七章 ACE同步事件分离封装.

                                                              Sidebar 14: Motivation for Nonblocking Acceptors

         当把一个Acceptor socket传递给select(),如果收到一个链接,那么该socket被标识为激活状态.很多服务器利用这个事件去表明可以调用accept(),不会引起阻塞.不幸的是,有来自TCP/IP异步行为的资源竞争.在实际中,select()之后就表明一个acceptor socket是激活的(但是在accept()调用之前),client可以关闭它的连接,一旦accept()阻塞潜在的挂起整个进程.为了避免这个问题,acceptor socket当和select()一起使用的时候,应该一直被设置为非阻塞的模式.在ACE中,这步工作很方便,只要传递ACE_NONBLOCK标识给enable()方法就可以了.enable()方法是ACE_IPC_SAP提供的,所以ACE_SOCK_Acceptor也拥有.
posted @ 2006-06-12 11:18 一动不如一静 阅读(586) | 评论 (0)编辑 收藏

1.最好先用ACE自身的例子,测试所编库的正确性.
2.确保ACE.lib或ACEd.lib能正确的链接到工程中.
3.如果出现"__declspec(dllimport) int __cdecl ace_os_main_i"字样的链接错误,可以考虑加上#include "ace/OS_main.h"
posted @ 2006-06-11 23:18 一动不如一静 阅读(26255) | 评论 (5)编辑 收藏

在类中声明成员方法的时候,只有纯虚函数才可以不用实现.
虚函数必须实现,否则会出现error LNK2001的错误.

posted @ 2006-06-11 23:06 一动不如一静 阅读(417) | 评论 (0)编辑 收藏

仅列出标题
共2页: 1 2