Chinaunix

标题: [c++]类和线程怎么结合{有码) [打印本页]

作者: inet_addr    时间: 2012-06-21 09:19
标题: [c++]类和线程怎么结合{有码)
  1. void * recieve_thread(void *args);

  2. class Server
  3. {
  4. public:
  5.     Server() {}
  6.     ~Server() {}
  7.    
  8.     bool setAddr(char *ip, unsigned short port);
  9.    
  10.     void run();
  11.    
  12. public:
  13.     int max;
  14.    
  15. private:
  16.     char ip[16];
  17.     unsigned short port;
  18.     pthread_t recv_tid;
  19. };


  20. void Server::run()
  21. {
  22.     pthread_create(&recv_tid, NULL, recieve_thread, this);
  23.    
  24.     return;
  25. }

  26. void * recieve_thread(void *args)
  27. {
  28.     Server *ser = (Server *)args;
  29.    
  30.     while(1)
  31.     {
  32.       recv_line(buf);
  33.       
  34.       max = MAX(max, atoi(buf));
  35.     }
  36.     return(NULL);
  37. }
复制代码
线程函数和类怎么结合, 多线程程序中主要逻辑都在线程函数内, 访问一个对象还好, 要是访问多个对象呢?

作者: bruceteen    时间: 2012-06-21 10:08
如果我没有理解错的话,你这个问题和“类”没有任何关系
你想问的是怎么在 pthread_create 中传进多个用户参数?
作者: bruceteen    时间: 2012-06-21 10:10
对于复杂点的,我一般是用个结构体封装一下所有用户参数
作者: inet_addr    时间: 2012-06-21 11:23
bruceteen 发表于 2012-06-21 10:08
如果我没有理解错的话,你这个问题和“类”没有任何关系
你想问的是怎么在 pthread_create 中传进多个用户 ...



线程函数体内的代码不在类的作用域中, 即使把线程函数设成类的成员也没用.


作者: bruceteen    时间: 2012-06-21 12:00
inet_addr 发表于 2012-06-21 11:23
线程函数体内的代码不在类的作用域中, 即使把线程函数设成类的成员也没用.

听不懂,代码 和 作用域 有啥关系?
作者: aychxm    时间: 2012-06-21 12:07
void * recieve_thread(void *args)
这里不是把类的地址传进来了么?
作者: sonicling    时间: 2012-06-21 12:20
static void * Server::recieve_thread(Server *args)
{
//... 这不就有了Server类的全部作用域
}

pthread_create(&recv_tid, NULL, (thread_proc)&Server::recive_thread, this);
作者: inet_addr    时间: 2012-06-21 14:06
sonicling 发表于 2012-06-21 12:20

static void * Server::recieve_thread(Server *args)
{
//... 这不就有了Server类的全部作用域
}

pthread_create(&recv_tid, NULL, (thread_proc)&Server::recive_thread, this);



void * Server::recieve_thread(Server *args)

Server ser;
pthread_create(&recv_tid, NULL, ser.recive_thread, NULL);

这些好像更自然, 可是编译都不过

   error: argument of type 'void* (Server:(void*)' does not match 'void* (*)(void*)'
作者: sonicling    时间: 2012-06-21 14:11
本帖最后由 sonicling 于 2012-06-21 14:12 编辑

强制转换一下啊,要不就在函数里面转换去。我是宁愿加一行typedef,也不想单独写一个转换语句。
作者: inet_addr    时间: 2012-06-21 14:28
sonicling 发表于 2012-06-21 14:11
强制转换一下啊,要不就在函数里面转换去。我是宁愿加一行typedef,也不想单独写一个转换语句。


typedef void * (*thread_fun)(void *);

转换是一方面, 那个线程函数必须是静态的, 我感觉不自然, 让c++的代码看着别扭.

静态函数不能访问私有成员, 虽然它(这个线程函数)本来就是这个类最关键的部分.
作者: sonicling    时间: 2012-06-21 15:30
回复 10# inet_addr


    静态函数只是个面向库的接口而已,是private不对外的。里面调用线程类的实际工作函数
  1. class thread
  2. {
  3. public:
  4.      virtual void run() = 0;
  5. private:
  6.      static void *thread_proc(thread *thrd)
  7.     {
  8.         thrd->run();
  9.     }
  10. };

  11. class my_thread : public thread
  12. {
  13. public:
  14.     void run()
  15.     {
  16.         // 这才是实际干事情的地方
  17.     }
  18. }

复制代码

作者: 群雄逐鹿中原    时间: 2012-06-21 15:38
尔等根本体会不到写C++的乐趣

  1. #include <my_pthread.h>
  2. #include <stdio.h>

  3. struct foo
  4. {
  5.     int n;
  6.     void *test(void *arg)
  7.     {
  8.         for(int i = 0; i < 10; ++i)
  9.             printf("%d \n", ++n);
  10.                 return NULL;
  11.     }
  12. };

  13. int main()
  14. {
  15.     foo f;
  16.     pthread_t id;
  17.     my_pthread_create(&f, &id, NULL, &foo::test, NULL);
  18.     pthread_join(id, NULL);
  19.     return 0;
  20. }
复制代码
以下为 my_pthread.h

  1. #ifndef INC_THREAD_PROXY_H_
  2. #define INC_THREAD_PROXY_H_

  3. #include <pthread.h>
  4. #include <semaphore.h>

  5. template <class T>
  6. class thread_proxy_imp
  7. {
  8. protected:
  9.     typedef struct
  10.     {
  11.         void *(T::*start_routine_)(void *);
  12.         void      *arg_;
  13.         T         *obj_;
  14.         sem_t      sem_;
  15.     } thread_param;

  16.     static void *thread_proxy_entry(void *arg)
  17.     {
  18.         thread_param *param_ptr = reinterpret_cast<thread_param *>(arg);
  19.         thread_param  param     = *param_ptr;
  20.         sem_post(&param.sem_);
  21.         return (param.obj_->*param.start_routine_)(param.arg_);
  22.     }

  23. public:
  24.     int create(
  25.         T                    *obj,
  26.         pthread_t            *thread,
  27.         const pthread_attr_t *attr,
  28.         void            *(T::*start_routine) (void *),
  29.         void                 *arg)
  30.     {
  31.         int rt;
  32.         thread_param param = {start_routine, arg, obj};
  33.         
  34.         sem_init(&param.sem_, 0, 0);
  35.         rt = pthread_create(thread,
  36.                             attr,
  37.                                                         thread_proxy_imp::thread_proxy_entry,
  38.                                                         reinterpret_cast<void *>(&param));
  39.         if(rt == 0) sem_wait(&param.sem_);
  40.         sem_destroy(&param.sem_);
  41.         return rt;
  42.     }
  43. };


  44. template<class T>
  45. int my_pthread_create(
  46.         T                    *obj,
  47.     pthread_t            *thread,
  48.     const pthread_attr_t *attr,
  49.     void            *(T::*start_routine) (void *),
  50.     void                 *arg)
  51. {
  52.     return thread_proxy_imp<T>().create(obj, thread, attr, start_routine, arg);
  53. }

  54. #endif



复制代码

作者: 群雄逐鹿中原    时间: 2012-06-21 15:56
nilgod 发表于 2012-06-21 15:39
目前看起来感觉使用lambda似乎看起来更优秀。


pthread_pthread 如何结合lambda用,请教。。
作者: 群雄逐鹿中原    时间: 2012-06-21 16:18
回复 16# nilgod

是吗,还是第一次有人这么批评我的。

那我等着,你有货拿出来遛遛,别太监了。
   
作者: 群雄逐鹿中原    时间: 2012-06-21 16:23
回复 19# nilgod

没事。我最喜欢看到真实的意见了。
不过,你说一塌糊涂和烂,要具体的指明烂之所在。
这个不影响你装系统吧。
   
作者: 群雄逐鹿中原    时间: 2012-06-21 16:33
nigod哑炮了?真是个二货。
作者: 群雄逐鹿中原    时间: 2012-06-21 16:44
晕啊,我不好在哪里啊,就凭你感觉?!真是个二货没错。。
作者: 群雄逐鹿中原    时间: 2012-06-21 16:46
回复 24# nilgod


    我原本等着你指出我的不足,好好切磋一下,
结果发现不靠谱。

你说 “第一是感觉,第二是你整个就不好。",
这是什么狗屁理由啊

作者: sonicling    时间: 2012-06-21 16:50
关注点不一样,封装自然不同。

只关注任务本身,不关心线程,像群雄逐鹿中原那么封装比较好。但是要对线程进行操作,自然是nilgod的封装比较好。

有一点是互通的,那就是用静态函数进行转接。
作者: 群雄逐鹿中原    时间: 2012-06-21 17:01
本帖最后由 群雄逐鹿中原 于 2012-06-21 17:06 编辑

好吧,为了让某些网友服气点,我再多罗嗦两点

1. 逐鹿在13楼的封装,很简单,
   就是针对楼主的需求,对于类里的线程函数,
   只要加上类对象 f 就行了,除此之外没有别的功能。
    my_pthread_create(&f, &id, NULL, &foo::test, NULL);
   几乎和原生的pthread_create函数一样用法,简单上手。
   
2. 某网友在22楼的代码,class和thread结合,
   里面有一个极其典型的bug,
   就是构造函数启动线程,线程调用虚函数。
   构造函数里调用:
      Start(process, stackSize, isSuspended);
   Start线程后,用virtual
      virtual DWORD Run();
   
   这个bug相当典型,并且是一般情况下不出现,偶尔出一下会要命,
   不注意的人极难找到。
   
为什么逐鹿如此肯定这个bug呢?
为什么逐鹿在13楼用小清新的封装办法呢?
你不被这样的事情折磨几次,不会明白的。
作者: 群雄逐鹿中原    时间: 2012-06-21 17:38
nilgod 是不是CU派过来活跃气氛的啊,哈哈
作者: byrsongQQ    时间: 2012-06-21 17:48
因为有BUG,所以nilgod的代码坏!

群雄逐鹿中原的代码好不好,不好说,因为没有人指出BUG。
作者: 群雄逐鹿中原    时间: 2012-06-21 17:50
nilgod 发表于 2012-06-21 17:43
不过我依然给你这种认为是bug的人留了独立的函数来操作,你不用那几个构造函数就是。


bug被指出来了,不知感恩。
反倒说这个函数你不要用就是。

这逻辑无敌了,不晓得什么人有幸和你同事,我真的太同情他们了。
作者: 群雄逐鹿中原    时间: 2012-06-21 17:57
byrsongQQ 发表于 2012-06-21 17:48
因为有BUG,所以nilgod的代码坏!
群雄逐鹿中原的代码好不好,不好说,因为没有人指出BUG。


非常有幸成为 nilgod 的坏代码,
但是很遗憾,他仍旧说不出哪里坏,
我从他后面的回帖里,找到的只有这几个说辞:

“第一是感觉”
“第二是你整个就不好。”
“依然你那个很烂”
“代码好坏,一看便知,其他都是借口”

到底哪里坏?
作者: aychxm    时间: 2012-06-21 18:08
好不容易在cu上看到点有意思的c++代码,这贴代码的人咋都这么牛逼哄哄呢?至于么
作者: 群雄逐鹿中原    时间: 2012-06-21 18:09
回复 40# nilgod

我就是随手一写,也比你写的好啊。
原汁原味的pthread风格,加一个参数就能套用到类里的线程,
几乎没有学习和重构代码的成本。
最大限度的保证原pthread代码的重用性。


而你这个重用就怪了,你那个构造函数,就是无法继承不能重用的,还有脸说,呵呵
TThread(uint64_t stackSize, bool isSuspended)
你若真的知道这是个bug的话,就不应该有isSuspended参数。

另外win32 thread不好重用?pthread不好重用?你包装一个有bug的代码给谁重用啊。
有的人,写一层包装,就是简化了工作接口;
有的人,写一层包装,只是多个更复杂的接口。


   
作者: 群雄逐鹿中原    时间: 2012-06-21 18:16
本帖最后由 群雄逐鹿中原 于 2012-06-21 18:16 编辑

回复 43# nilgod


    貌似是你在16楼先攻击我的。
本来攻击无所谓,你要说出理由我虚心接受(可是直到40楼才有实质理由,理由靠不靠得住另当别论),
前面你不说理由,只骂各种“烂”,我只能认为你是攻击我,只有反击喽。。。

作者: 塑料袋    时间: 2012-06-21 18:27
群雄逐鹿中原 发表于 2012-06-21 18:16
回复 43# nilgod


你的代码很烂,因为你这个人根本不自知。

面对上nilgod,你是没有一点点占便宜的希望的,醒醒吧。


nilgod是什么人???已经无敌了!!!  你这愚蠢的地球人啊

waleeee的发言:
waleeee退出CU的声明!
waleeee退出CU的补充声明!
waleeee退出CU的补充的补充声明!

nilgod在这以后出现在C版,其发言有:
我不是waleeee,不过waleeee是我同学
我不是waleeee,不过waleeee坐在我旁边
我不是waleeee,不过waleeee托我对你说这些话
我不是waleeee,不过waleeee托我问候你

作者: 群雄逐鹿中原    时间: 2012-06-21 18:31
46楼我看出点感觉来了,md有种被sm鞭抽的愉悦感。

塑兄提示的及时。我知错了,我代码确实是烂,的确烂,非常烂。

作者: 群雄逐鹿中原    时间: 2012-06-21 18:37
回复 48# nilgod


拿回的慢掩饰你是个二货,真说不过去。

你在16楼、19楼骂过代码烂,
直到21楼我才指出你这个二货。

有时间再次说代码烂,没时间解释下原因?
二货就是二货。
作者: byrsongQQ    时间: 2012-06-21 21:04
回复 47# 塑料袋



太可怕了。。。


   
作者: linccn    时间: 2012-06-21 22:03
群雄逐鹿中原 发表于 2012-06-21 15:38
尔等根本体会不到写C++的乐趣以下为 my_pthread.h
看着舒服。
比下面的win好多了

作者: 群雄逐鹿中原    时间: 2012-06-21 23:14
大号是瓦力?我看瓦力也没这么恶劣吧
作者: 群雄逐鹿中原    时间: 2012-06-21 23:19
本帖最后由 群雄逐鹿中原 于 2012-06-21 23:24 编辑

舒服在哪里,好在哪里。
----------------------------
你个二货,不要为难那位网友了,还是我来给你解释吧。
(二货放心,我解释起来绝对言之有物,不会因为烂所以烂胡搅蛮缠)

至少好在个地方:
1. 最大程度的保持pthread_create接口,减少使用者学习负担,减少接口多了混乱的顾虑。
2. 非侵入性的适配不同的class,比你那个模块间的耦合性更低。
   你那个,需要强迫class的继承关系。你中OO的毒了。
3. 不限制class的线程入口函数,调用时可以指定不同的函数,用起来方便。
    你那个,只能用一个函数当入口(Run函数)。
4. 无bug。
   而你那个bug,离谱到线程可能根本起不来。

作者: 群雄逐鹿中原    时间: 2012-06-21 23:26
回复 60# nilgod

你有什么资格要求别人的尊重,呵呵。
用你的话说,我写我的,是我的自由,你看不看随你。二货!

   
作者: 塑料袋    时间: 2012-06-21 23:28
nilgod 发表于 2012-06-21 23:06
回复 47# 塑料袋

要我大号现身么?妈的


你现身啊,你 妈 个 逼 的,今天我就跟你过不去了!
作者: linccn    时间: 2012-06-22 00:03
本帖最后由 linccn 于 2012-06-22 00:12 编辑

回复 55# nilgod
群雄逐鹿中原 发表于 2012-06-21 23:19


    template <class T>
    class thread_proxy_imp {};
这个间接层做得很好。

   my_pthread_create(&f, &id, NULL, &foo::test, NULL);
使用起来非常自然。(大概要把“my”排除在外)

以上两点就很舒服。

在你的代码里体会不到这种愉悦感,不好意思。


作者: zyzbill    时间: 2012-06-22 10:23
本帖最后由 zyzbill 于 2012-06-22 10:31 编辑

群雄逐鹿中原 - 你的代码是是什么时候编写的? 具体时间?
如果说你的那段代码是你自己编写的,那么google代码库里面的代码是你贡献的么?

http://xmpp-library.googlecode.c ... thread_proxy_impl.h
作者: 三月廿七    时间: 2012-06-22 15:52
本帖最后由 三月廿七 于 2012-06-22 16:12 编辑

笑而不语

   
作者: 群雄逐鹿中原    时间: 2012-06-24 13:58
本帖最后由 群雄逐鹿中原 于 2012-06-24 14:02 编辑


xmpp关于thread_proxy这段,不是移除了版权,是增加了版权。
这段代码2008年6月6日,始发于CU,是我的另一个帐号发的。

http://xmpp-library.googlecode.com/svn/trunk/util/这个,
是2008年7月1日提交的,跟我没有关系。
别人抄我的,我没话可将。



作者: 群雄逐鹿中原    时间: 2012-06-24 14:00
代码里可以随便写,何时create的,没关系。
svn真是个好工具,什么时候提交的,无法赖账。

作者: 群雄逐鹿中原    时间: 2012-06-24 14:01
回复 67# zyzbill

不清楚这个项目怎么回事。



   
作者: OwnWaterloo    时间: 2012-06-24 14:20
回复 13# 群雄逐鹿中原

如果将obj_与start_routine_砍掉,就不需要一个用于打包的thread_param,自然也不需要用于同步的sem_。
那么对每个 void* (T:: *f)(void) 只需要产生对应的 void* (*)(void* x) { return static_cast<T*>->f(); }。
然后pthread_create(&id, 0, member_function<T,T:: f0>::to_plain, &obj);

缺点:
1. 限制了foo:: test不能再接受参数,只能是void* foo:: f0(void) 而不是void* foo:: test(void*)。
2. 将void* (T:: *f)(void) 作为编译时参数产生的代码是per f。而thread_proxy_imp产生的代码是per T。

优点是代价小得多……
为per f产生的代码其实就只有一个跳转指令。而thread_proxy_imp产生的代码虽然与该T有多少个成员函数n会被部分应用无关,是n的常数。但常量因子大得多……
也不再需sem_(或者启动者在堆上分配,被启动者释放)作为同步。

作者: 群雄逐鹿中原    时间: 2012-06-24 14:28
回复 74# OwnWaterloo

嗯,也考虑过不要同步的sem_,
是这样想的:
thread_param 参数用 new/malloc,动态分配出来到线程里释放。

但是个人习惯,还是更不喜欢用动态分配。
作者: OwnWaterloo    时间: 2012-06-24 14:35
回复 75# 群雄逐鹿中原

如果
1. 不需要obj_ —— arg就是obj,只能从void* (T:: *f0)(void) 转换到 void* (*)(void*),而不是将void* (T:: *f1)(void*)部分应用得到void* (*)(void*)
2. start_routine_ 是一个编译时值。

那sem_或new/malloc都可以不用哇……

就是一个很普通转发的函数:

  1. void* f(void* x) { return static_cast<T*>(x)->f(); }
复制代码
只是用模板将f与T参数化避免一次又一次实现而已……

作者: 群雄逐鹿中原    时间: 2012-06-24 14:51
回复 76# OwnWaterloo

很多时候有了class,最后一个arg参数就没有必要,
这样的确效率更好一点。

函数入口的问题,也是可以解决的。
静态函数的话,直接作为模板参数就OK了。



  1. #include <pthread.h>
  2. #include <semaphore.h>

  3. template <class T, void *(T::*start_routine)()>
  4. class thread_proxy
  5. {
  6. public:
  7.         static void *thread_proxy_entry (void *arg)
  8.         {
  9.                 T *obj = reinterpret_cast<T *> (arg);
  10.                 return (obj->*start_routine)();
  11.         }

  12. };


  13. template <class T, void *(T::*start_routine)()>
  14. int my_pthread_create (
  15.         T *obj,
  16.         pthread_t *thread,
  17.         const pthread_attr_t *attr)
  18. {
  19.         return ::pthread_create(thread, attr, &thread_proxy<T, start_routine>::thread_proxy_entry, reinterpret_cast<void *> (obj));
  20. }

  21. #include <stdio.h>
  22. struct foo
  23. {
  24.     int n;
  25.     void *test1()
  26.     {
  27.         for(int i = 0; i < 10; ++i)
  28.             printf("test1: %d \n", ++n);
  29.                 return NULL;
  30.     }
  31.     void *test2()
  32.     {
  33.         for(int i = 0; i < 10; ++i)
  34.             printf("test2: %d \n", ++n);
  35.                 return NULL;
  36.     }       
  37. };

  38. int main()
  39. {
  40.     foo f;
  41.     pthread_t id;

  42.     my_pthread_create<foo, &foo::test1>(&f, &id, NULL);
  43.     pthread_join(id, NULL);

  44.     my_pthread_create<foo, &foo::test2>(&f, &id, NULL);
  45.     pthread_join(id, NULL);

  46.     return 0;
  47. }
复制代码

作者: OwnWaterloo    时间: 2012-06-24 15:12
回复 77# 群雄逐鹿中原

按照59楼的说法,我觉得其实my_pthread_create也可以砍掉。。。直接用thread_proxy就完了。
- my_pthread_create<foo, &foo::test1>(&f, &id, NULL);
+ pthread_create(&id, 0, thread_proxy<foo, &foo:: test1>:: thread_proxy_entry , &f);

thread_proxy已经没有与thread耦合了。它的工作仅仅是从非静态成员函数得到一个普通C函数。只是这个签名恰好符合创建thread而已。
如果有其他的C库需要void* (*)(void*)的callback,一样可以通过thread_proxy来转。

作者: 群雄逐鹿中原    时间: 2012-06-24 15:47
回复 78# OwnWaterloo


    如能想办法将这句简化下,没有累赘,就功德圆满了
thread_proxy<foo, &foo:: test1>:: thread_proxy_entry
作者: 群雄逐鹿中原    时间: 2012-06-24 17:54
搞出这么个效果,大家不要觉得恶心。
调用方式有点怪异

pthread(对象).create<&对象中的函数>(&id, &attr);

例如对于对象f和f.test1()函数,调用方式如下:
pthread(f).create<&foo::test1>(&id, NULL);


比起13楼的版本,
好处是没有多余的semaphore,代码效果完全无多余计算。

  1. #include <pthread.h>
  2. #include <semaphore.h>

  3. template <class T>
  4. struct thread_proxy_imp
  5. {
  6.         T *obj_;

  7.         thread_proxy_imp(T *obj)
  8.                 : obj_(obj)
  9.         {
  10.         }
  11.        
  12.         template<void *(T::*start_routine)()>
  13.         static void *thread_proxy_entry (void *arg)
  14.         {
  15.                 T *obj = reinterpret_cast<T *> (arg);
  16.                 return (obj->*start_routine)();
  17.         }
  18.        
  19.         template<void *(T::*start_routine)()>
  20.         int create(
  21.                 pthread_t *thread,
  22.                 const pthread_attr_t *attr)
  23.         {
  24.                 ::pthread_create(thread, attr, &thread_proxy_entry<start_routine>, reinterpret_cast<void *>(obj_));
  25.         }
  26. };

  27. template <class T>
  28. inline thread_proxy_imp<T>
  29. pthread(T &obj)
  30. {
  31.         return thread_proxy_imp<T>(&obj);
  32. }

  33. #include <stdio.h>
  34. struct foo
  35. {
  36.     int n;
  37.     void *test1()
  38.     {
  39.         for(int i = 0; i < 10; ++i)
  40.             printf("test1: %d \n", ++n);
  41.                 return NULL;
  42.     }
  43.     void *test2()
  44.     {
  45.         for(int i = 0; i < 10; ++i)
  46.             printf("test2: %d \n", ++n);
  47.                 return NULL;
  48.     }       
  49. };

  50. int main()
  51. {
  52.     foo f;
  53.     pthread_t id;

  54.     pthread(f).create<&foo::test1>(&id, NULL);
  55.     pthread_join(id, NULL);

  56.     pthread(f).create<&foo::test2>(&id, NULL);
  57.     pthread_join(id, NULL);

  58.        
  59.     return 0;
  60. }


复制代码

作者: sonicling    时间: 2012-06-24 22:15
为什么要把函数指针放在模板参数里?放在函数参数里不是更简洁吗?
作者: OwnWaterloo    时间: 2012-06-24 22:48
回复 79# 群雄逐鹿中原

可以用宏么……PLAIN(T,f) (plain<T,&T::f>)……
作者: OwnWaterloo    时间: 2012-06-24 22:58
回复 80# 群雄逐鹿中原

我倒是不觉得恶心或怪异…… 先用function template进行deduce,避免写T[2],顺带获取obj参数[1]……

[1] 因此,还是与thread_create耦合在一起了……
[2] 其实还是有冗余信息
pthread(f).create<&foo:: test1>(&id, NULL);
&foo:: test1 中的foo, 它是f的类型, 如果C++能设计得更科学一些, 应该是不需要写的……

@幻の上帝有什么高招没……

作者: OwnWaterloo    时间: 2012-06-24 23:11
回复 27# sonicling

什么叫对线程进行操作?
原本C提供ADT做成一个C++ class,将与该ADT配套的APIs逐一为之写一个non static member function并转发到原API
我是想不明白这样的代码写着有什么意思,又有什么好处……

作者: OwnWaterloo    时间: 2012-06-24 23:22
回复 28# 群雄逐鹿中原

你还真有耐心……

要是我看到这种极其不对自己胃口的冗长代码,根本就没心思去看。
全文一搜_beginthreadex,哦,没有。
再因为创建线程不是手段 —— 比如向其他进程注入代码,可以通过创建远程线程的方式,这时候创建线程只是达到目的的手段,被创建的线程会做什么是清楚的 —— 代码的目的是为了让代码用户能创建他自己希望的线程, 那么bug无误。


还有,你有没有发现你的问题已经被悄悄的转移了?
LZ需要解决的问题的关键是non static member function -> static member function(or plain C function),是需要作为函数指针(pointer to function)传递给其他函数的。
这根本不是C++ lambda能解决的问题,lambda创建的始终是function object
对抓着一些自己弄不清楚的名词就开始叽叽喳喳的,你耐心过头了……
作者: sonicling    时间: 2012-06-24 23:51
回复 84# OwnWaterloo


    可以算作object-based设计,就跟MFC一样。这算不算是一种意思?

如果这算是一种强迫症的话,玩模板的通常有更深的强迫症。
作者: 群雄逐鹿中原    时间: 2012-06-25 00:06
sonicling 发表于 2012-06-24 22:15
为什么要把函数指针放在模板参数里?放在函数参数里不是更简洁吗?


函数参数的话,需要通过pthread_create的最后一个参数传递。
放在模板参数里,就可以直接通过 “编译” 途径传递。
于是在这里节省了一个函数参数,可以少用一个semaphore或malloc.

作者: 群雄逐鹿中原    时间: 2012-06-25 00:12
回复 85# OwnWaterloo

又给你看到一个bug,
Win32下面调用CreateThread几乎都可以认为是bug,这个确认无疑。
(几乎表示有例外,此例外不予考虑)

lambda给那小子糊弄过去了,在15楼追问下,是有点怀疑的。



作者: sonicling    时间: 2012-06-25 00:15
回复 87# 群雄逐鹿中原


    嗯,缺点就是模板参数必须提供不可推导。
作者: 群雄逐鹿中原    时间: 2012-06-25 00:30
OwnWaterloo 发表于 2012-06-24 23:11
我是想不明白这样的代码写着有什么意思,又有什么好处……


这句真是点到痛处了。
好处就是。。除了可以继续隐含用this外, 没有实质好处。
作者: OwnWaterloo    时间: 2012-06-25 01:58
回复 86# sonicling

object-based设计……

如果是从C++开始进行object-based设计,我个人只是不喜欢,还不至于到妄加评论。
但我问题的前提是:在C已经有ADT与对应的一套APIs的情况下。

设计的目的是什么?在此前提下继续再次进行object-based设计又能得到什么?
这只是为设计而设计,就像你说的,强迫症。



玩模板的确实也容易走火入魔。要将特性滥用是很容易的事情。
如果抛开那些耍小聪明的代码,只说特性用到恰到好处的话,模板与object-based本质不同点是:C语言可以胜任object-based,C++在这方面只有少量改进;但C语言无法胜任C++在模板方面的许多工作。
所以C库是有能力完成object-based的设计。回到前提,对这种C库,再次设计一次不会有什么收获,


至于MFC……  那一大堆的成员函数用自己的m_hWnd转发到win32 gui的代码…… 有意思吗…… 尤其是它还有operator HWND……
F(x,args...)的调用就这么难看,难看到值得花大力气 —— 实现的人先花力气写了,然后又让用的人花力气再学一次 —— 将其"封装"成x.F(args...)才行吗……

作者: 群雄逐鹿中原    时间: 2012-06-25 10:36
本帖最后由 群雄逐鹿中原 于 2012-06-25 10:40 编辑

模板是好玩的东西,不玩模板的程序员,不是真正的C++程序员。
作者: sonicling    时间: 2012-06-25 12:14
回复 91# OwnWaterloo


    有些东西C接口是不提供的。比如每个线程有一组相关的数据,把他们包装到一起,每次得到线程指针的时候,也同时得到了相关的数据,多好,不用再调用额外的API去查询什么了。就跟字符串一样,字符串的核心不就是char *吗,C标准库已经提供了一组关于char *的函数了,为啥还要包装成string?还不是包装后更好用,得到一个string,不光得到的是那个char *,还得到了长度等信息,虽然大家都知道最终操纵的就是里面那个char *,甚至调用的就是C标准库。

我还包装过socket,把socket和之上未完成的任务包装在一起,下次select出来的时候继续,任务就是应用层协议的具化。
作者: OwnWaterloo    时间: 2012-07-04 17:56
回复 62# sonicling

关于char*与string。
1. 如果你是指string本身

这就不是我说的将C api换汤不换药的搞成C++ class了。
char*与string之间的区别,相比:

  1. class file {
  2.       FILE* p_;
  3. public:
  4.       ... fread(...) { return ::fread(...); }
  5. };
复制代码
与FILE*之间的区别,这能比吗?
我说的是后一对这种情况。

2. 如果你是指在object-based化时顺带将string作为参数。

比如:

  1. file::printf(std::string const& fmt, ...)
复制代码
甚至

  1. class file {
  2. public:
  3. static int remove(std::string const& f) { ... }
  4. };
复制代码
那这种所谓的objecte-based化同样没有意义,可以去除。实现一个重载并使用string作为参数就可以了。

  1. int remove(std::string const& f) { return remove(f.c_str()); }
复制代码
string与char*是很不相同的概念,分别使用它们作为参数的remove也是稍有区别的。
但file这种东西,真要说它和FILE*有不同,那也就是一层纸的区别。

其实就算是remove(string)这种代码,我都不太愿意做。
这种称谓"垫片"的技巧虽然用起来确实可能会很方便,但会让两个模块间交互的方式陡增且混乱。
这也是C++会被C程序员(嗯,@starwing83,我就是在说你)批评"不干净"的一个原因。
如果真要这么做,那我肯定要尽可能地 —— 文档、命名空间、不同头文件等许多方式 —— 让用户知道哪些是primitive,哪些是auxiliary。


关于线程、socket,可能性太多,若像上面那样对每种猜测都作解释就太麻烦了…… 还请明示……

作者: zylthinking    时间: 2012-07-04 18:55
群雄逐鹿中原 发表于 2012-06-21 15:38
尔等根本体会不到写C++的乐趣以下为 my_pthread.h


才看到, 这一通戏法变的




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2