免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: duanjigang
打印 上一主题 下一主题

[C++] 推荐我写的一个C++多线程程序开发库 [复制链接]

论坛徽章:
0
11 [报告]
发表于 2007-06-01 14:21 |只看该作者
原帖由 醉卧水云间 于 2007-6-1 12:47 发表
看了几分钟, 简评一下:
#ifdef __DEBUG_ ...

首先:
    printf不用lock在多线程的程序里面打印确实是会出现乱码的,我是先发现打印的乱码问题才加入Lock的。
其次:
     位置不对,是否应该写成这样
     

  1. #ifdef __DEBUG__
  2.                 LockOn(&LockPrint);
  3.                 printf(szMsg);
  4.                 LockOff(&LockPrint);
  5. #endif
  6.          
复制代码

如果是的话,原来的写法也是行得通的,只不过在无有__DEBUG__宏的时候空Lock了一次
并不会造成错误,不过考虑细致点的话,赞成你的说法。
另外:
  CWorkThread类的意图不知道楼上体会到没有,而是尽量让CWorkThread类的线程体跟具体的任务不要相关,把具体的任务跟CTask绑定了。
其实这个模型的最初样子是:
多个工作线程启动运行,任务对列中的任务是由主线程动态添加进去的,这样就能处理诸如消息转发,数据处理等发生效率比较高的任务,也就是任务对象的个数不限制或者未知的情况,只是由于那样做的话,CWorkThread要牵扯到释放内存,这个我还没有处理好,所以先作成现在这个模型。
最后:
   initnetwork那个实现确实是由于我才疏学浅对windows下的一些东西并不是很懂,才勉强作成那样的。还有Lock这个操作到目前为止,只牵扯到Lock和UnLock两个方法,所以偷懒没有作成类。个人认为没有必要这样做
关于实现上的问题应该是比较容易修改的,但是类的设计上确是很重要的,如果CWorkThread类封装古怪,希望楼上给点意见,指出哪里不太合适,如果有更好的设计,欢迎赐教!

[ 本帖最后由 duanjigang 于 2007-6-1 14:34 编辑 ]

论坛徽章:
0
12 [报告]
发表于 2007-06-01 15:05 |只看该作者

添上一个读写者的例子

代码中带的UDP的例子实际上模拟的是两个进程
做了一个读写者的例子(直接将rw目录解压到cucme目录下进入rw目录make就可),放上来,Write向string队列中不定期写入数据,Read不定期从string队列中读取消息,写20次,读20次,使用了一个Lock实现资源互斥访问
写者的执行函数

  1. int CWriteTask::Execute()
  2. {
  3.         SLEEP(rand()%3, 500);
  4.         char szMsg[128];
  5.         string str;
  6.         sprintf(szMsg, "告诉你%d次了,不要乱扔东西\n", ++m_nWriterCounter);
  7.         LockOn(g_newLock);
  8.         g_strQueue.push(string(szMsg));
  9.         LockOff(g_newLock);
  10.         str = "写入:" + string(szMsg);
  11.         theLogger.LogMessage(str, true);
  12.         return 1;
  13. }
复制代码

读者的执行函数

  1. int CReadTask::Execute()
  2. {
  3.         SLEEP(rand()%5, 300);
  4.         string str;
  5.     if(!g_newLock)
  6.         {
  7.                 return 1;
  8.         }
  9.         LockOn(g_newLock);
  10.         if(!g_strQueue.empty())
  11.         {
  12.                 ++m_nReaderCounter;
  13.                 str = g_strQueue.front();
  14.                 g_strQueue.pop();
  15.         }
  16.         LockOff(g_newLock);
  17.         if(str != "")
  18.         {
  19.                 str = "读取:" + str;
  20.                 theLogger.LogMessage(str, true);
  21.         }
  22.         return 1;
  23. }
复制代码

makefile

  1. #!/bin/sh
  2. default:
  3.         g++   -D__LINUX__ -c  -I ../include *.cpp
  4.         g++ -o rw *.o  -L /usr/local/lib  -lTask -lpthread -ludp -lLogger
  5. clean:
  6.         rm -fr *.o rw
复制代码

通过WriteTask.(h/cpp)和ReadTask.(cpp/h)四个类文件,我们只要编译后的o文件连接进库的主框架就能执行了
说心里话,俺还是比较喜欢这种模型的,不必去管程序入口,一心做好线程要做的事情就是了 ^_^

[ 本帖最后由 duanjigang 于 2007-6-1 15:09 编辑 ]

rw.rar

1.69 KB, 下载次数: 91

论坛徽章:
38
2017金鸡报晓
日期:2017-02-08 10:39:4215-16赛季CBA联赛之深圳
日期:2023-02-16 14:39:0220周年集字徽章-年
日期:2022-08-31 14:25:28黑曼巴
日期:2022-08-17 18:57:0919周年集字徽章-年
日期:2022-04-25 13:02:5920周年集字徽章-20	
日期:2022-03-29 11:10:4620周年集字徽章-年
日期:2022-03-14 22:35:1820周年集字徽章-周	
日期:2022-03-09 12:51:3220周年集字徽章-年
日期:2022-02-10 13:13:4420周年集字徽章-周	
日期:2022-02-03 12:09:4420周年集字徽章-20	
日期:2022-01-25 20:14:2720周年集字徽章-周	
日期:2022-01-13 15:12:33
13 [报告]
发表于 2007-06-01 18:00 |只看该作者
首先:
    printf不用lock在多线程的程序里面打印确实是会出现乱码的,我是先发现打印的乱码问题才加入Lock的。


我看到ThreadSafePrint这个函数就已经皱眉头了. 写这个函数本身就说明你没有理解VC的库. 你写的是multi-thread库, 却和vc的single-thread库连接, printf能不出错?

其次:
     位置不对,是否应该写成这样


CODE:[Copy to clipboard]#ifdef __DEBUG__
                LockOn(&LockPrint);
                printf(szMsg);
                LockOff(&LockPrint);
#endif


为什么不全包括?


  1. #ifdef __DEBUG__
  2.         if(!Initted)
  3.         {
  4.                 InitLock(&LockPrint);
  5.                 Initted = true;
  6.         }
  7.         LockOn(&LockPrint);

  8.         printf(szMsg);

  9.         LockOff(&LockPrint);   
  10. #endif      
复制代码

如果是的话,原来的写法也是行得通的,只不过在无有__DEBUG__宏的时候空Lock了一次
并不会造成错误,不过考虑细致点的话,赞成你的说法。


你是在写库, 不是写应用程序, 库里面一大堆无用代码影响太大.

另外:
  CWorkThread类的意图不知道楼上体会到没有,而是尽量让CWorkThread类的线程体跟具体的任务不要相关,把具体的任务跟CTask绑定了。
其实这个模型的最初样子是:
多个工作线程启动运行,任务对列中的任务是由主线程动态添加进去的,这样就能处理诸如消息转发,数据处理等发生效率比较高的任务,也就是任务对象的个数不限制或者未知的情况,只是由于那样做的话,CWorkThread要牵扯到释放内存,这个我还没有处理好,所以先作成现在这个模型。
最后:
   initnetwork那个实现确实是由于我才疏学浅对windows下的一些东西并不是很懂,才勉强作成那样的。还有Lock这个操作到目前为止,只牵扯到Lock和UnLock两个方法,所以偷懒没有作成类。个人认为没有必要这样做
关于实现上的问题应该是比较容易修改的,但是类的设计上确是很重要的,如果CWorkThread类封装古怪,希望楼上给点意见,指出哪里不太合适,如果有更好的设计,欢迎赐教!


看了CWorkThread, mutex的实现, 其他部分我都没太细看, 但已经可以看出很多问题了. 如果你看过一些成熟的跨平台线程实现再来对比你的实现应该可以发现差距, CWorkThread没有重用性, mutex没封装等等, 建议你多看看别人的代码然后再写库, 库比程序的要求更高更严格. 象printf输出乱码的问题是基础, 你的基础有点不牢. 库对你来说还是偏难的任务, 对我也一样

论坛徽章:
0
14 [报告]
发表于 2007-06-01 18:10 |只看该作者
多谢“醉卧水云间 ”的指点,我继续改进吧,不管难与不难,都是该努力的方向啊


[ 本帖最后由 duanjigang 于 2007-6-1 18:12 编辑 ]

论坛徽章:
0
15 [报告]
发表于 2007-06-02 10:33 |只看该作者
原帖由 duanjigang 于 2007-6-1 11:09 发表

霜霜给脸啊,不过我现在还不能教mm,我在学习中。。。。


  恩恩。。那学会了一定要教偶啊。。。。

论坛徽章:
0
16 [报告]
发表于 2007-06-02 12:24 |只看该作者
概述一下原理和每个类的作用
(1): CTask 一个任务基类,作为线程的参数用

值得商榷.

(2): CWorkThread:工作线程类,轮询检测参数是否可用,如果可用的话,
     就去调用参数CTask的执  行函数Execute,如果参数不可用就等待

轮训?自我调度?干嘛不是thread pool调度worker thread, worker thread再从thread pool里面的queue manager的task pool(也可以防在thread pool)里面take一个task,再执行一个command呢?
(3): CWorkQueue:是一个任务队列,里面装载CTask子类的对象,是一个对象的容器

  thread queue和thread pool的关系需要很好的考虑,比较复杂.
(4): CWorkThreadPool:工作线程池,其中包含了工作线程组,调度线程,调度线程不停的检测
     任务队列中是否有可用任务对象,如过取到一个有效的任务对象,就从工作线程组中查找出来
      一个空闲的工作线程,把这个任务交给这个空闲的线程去执行,如果没有空闲线程则创建新
     的工作线程并添加到工作线程组中。

调度线程纯粹多余,thread pool本身就是自适应的,他自己本身就是scheduler.另外"从工作线程组中查找出来
      一个空闲的工作线程"更是值得商榷?什么叫查找?这得多大开销呀.

论坛徽章:
0
17 [报告]
发表于 2007-06-06 13:48 |只看该作者
bool CSimpleUDP::InitRcvSock(const int nRcvPort)
{
        int sockfd;
                bool bRet = true;
        struct sockaddr_in addr;
       
        do {
                //创建套接字描述符
               if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
                       {
                        bRet = false;
                                                break;
                        }

                        //绑定IP地址和端口
                memset((void*)&addr, '\0', sizeof(struct sockaddr_in) );
                       addr.sin_family = AF_INET;
               addr.sin_addr.s_addr = htons(INADDR_ANY);
                       addr.sin_port = htons(nRcvPort);
                       //进行绑定
             if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
                       {
                        bRet = false;
                        break;
               }
               }while(false);
       
        //赋值套接字描述符的值
        m_nSock = sockfd;
        return bRet;
}



   如果创建socket失败和绑定失败就退出循环, 那这个循环有什么意义呢?

[ 本帖最后由 jiucailiuxing 于 2007-6-6 13:51 编辑 ]

论坛徽章:
0
18 [报告]
发表于 2007-06-06 15:05 |只看该作者

回复楼上

仔细分析了一下,上面的代码应该改成这样就更合适了

  1. bool CSimpleUDP::InitRcvSock(const int nRcvPort)
  2. {
  3.         int sockfd;
  4.         bool bRet = false;
  5.         struct sockaddr_in addr;
  6.         do
  7.         {
  8.                
  9.                 if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) break;
  10.                 addr.sin_family = AF_INET;
  11.                 addr.sin_addr.s_addr = htons(INADDR_ANY);
  12.                 addr.sin_port = htons(nRcvPort);
  13.                 if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
  14.                 {
  15.                         break;
  16.                 }
  17.                 bRet = true;
  18.                 m_nSock = sockfd;
  19.         }while(false);
  20.         return bRet;
  21. }
复制代码

听以前的一个同学分析说这么做可以把冗长的语句序列模块化,比如类似于

  1. socket....
  2. bind...
  3. listen...
  4. accept
复制代码

这样的大段程序,可以用一个

  1. do
  2. {

  3. }while(false)
复制代码

把它模块化,感觉好像有点道理,就这么写了
现在我发现这种做法的一个好处是,它可以替代goto语句直接跳转到一段程序的结尾
对于这个函数你可能觉得并没有必要,因为它的返回值为bool,直接返回false就行了
但是对于返回为void类型,并且出错时需要事务回滚或者出错处理的,这种做法就比较合适了,你可以在
while(false)
之后作出错处理,要是不出错就在
do
{
}
的结尾处返回
不知道这样的说法是否合理?

论坛徽章:
0
19 [报告]
发表于 2007-06-06 18:03 |只看该作者
支持一下,收藏。
需要的时候再看一下

论坛徽章:
0
20 [报告]
发表于 2007-06-20 15:00 |只看该作者
支持一个先..
直接用ACE不就可以了?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP