【原创】modb 功能设计之“跨线程通信”

简介:
【第一个版本】 
      使用 pipe 作为跨线程通信方式,使用如下代码来支持 pipe:
?
1
2
3
4
5
6
# 使用 _pipe 来模拟 pipe
#if defined(WIN32) || defined(_WIN32)
#include <io.h>
#include <fcntl.h>
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
#endif

      此时运行实现了 sql 线程的 demo,发现 sql 线程总是无法正常运行,在调用 libevent 接口 event_base_dispatch 时会有错误 “10038” 报出。最终查出的结论是:

?
1
WSAENOTSOCK                    (10038)             Socket operation on non-socket.

操作试图不是在套接字上进行。它可能是套接字句柄参数没有引用到一个合法套接字,或者是调用 select() 函数时,一个 fd_set 中的成员不合法。

      查来查去,结果发现,就是 windows 下不能将 pipe(其实是 _pipe)和 select 一起使用的原因。因为 windows 平台上会认为 pipe 产生的 fd 不是一个合法的 socket 句柄,在将读端 fds[0] 添加进 libevent 进行可读监听后(在 windows 平台下 libevent 使用 select 进行 fd 的检测),就会报出上面的 “10038” 错误;与此对应的,linux 下认为“一切皆文件”,所以是没有这个问题的。这让我再一次鄙视了 MS Windows。  

_pipe() 的实现如下:
?
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
int __cdecl _pipe (
         int phandles[2],
         unsigned psize,
         int textmode
         )
{
     int handle0, handle1;
     HANDLE ReadHandle, WriteHandle;
     ...
     if (!CreatePipe(&ReadHandle, &WriteHandle, &SecurityAttributes, psize)) {  // 调用 win api 创建 pipe
         ...
         return -1;
     }
     /* now we must allocate C Runtime handles for Read and Write handles */
     if ((handle0 = _alloc_osfhnd()) != -1) {   // 获取可用的 C 运行时句柄用作 pipe 的读端
         ...
         if ((handle1 = _alloc_osfhnd()) != -1) {  // 获取可用的 C 运行时句柄用作 pipe 的写端
         ...
         _set_osfhnd(handle0, ( intptr_t )ReadHandle);  // 将 CRT 文件句柄和 Win32 HANDLE 进行关联
         _set_osfhnd(handle1, ( intptr_t )WriteHandle);
         }
     }
     ...
     phandles[0] = handle0;
     phandles[1] = handle1;
      
     return 0;
}

      没办法,因为有 windows 下调试的需要,既然在 windows 下无法对非 socket fd 进行 select,那么可选方案只有通过 socket 来实现 pipe 了,备选方案如下:

  1. 通过 socket 模拟 pipe 的实现
  2. 使用 socketpair 实现(同样是通过 socket 来模拟 unix 下的 socketpair)
PS:另外一个出现的错误情况是,windows 下再调用 select() 前,需要先调用 WSAStartup(),否则会报出 “10093” 错误。 
?
1
WSANOTINITIALISED           (10093)             Successful WSAStartup() not yet performed.

应用程序没有调用 WSAStartup() 函数,或函数 WSAStartup() 调用失败了。应用程序可能访问了不属于当前活动任务的套接字(例如试图在任务间共享套接字),或调用了过多的 WSACleanup() 函数。 


【第二个版本】  
      翻阅大量开源软件的代码后,经过 对比发现后发现,两者的实现代码基本相同,只是名字不同而已。换句话说,通过 socket 模拟出来的这个东东,既可以替代 pipe(通常用于具有父子关系的进程间通信)的功能,也可以替代 socketpair(通常用于 AF_UNIX 域)的功能。  

      虽然上面说过,使用 socket 模拟 pipe 和模拟 socketpair 在代码实现上是没有差别的,但并不是说 linux 下的 pipe 和 socketpair 没有差别,所以下面的讨论以实现 pipe 为例。   具体代码可以参考我之前写过的一篇博客:《   MySQL Proxy中socketpair的使用   》。  

      在采用 socket 实现的 pipe 后,首先遇到的问题是,调用 write( sql_event_thread->notify_send_fd, "", 1 ) 会报如下断言错误:  
?
1
2
3
vctools\crt_bld\self_x86\crt\src\write.c
Line: 67
Expression: (fh >= 0 && (unsigned)fh < (unsigned)_nhandle)

其中 fh 为程序创建的 socket 句柄的值(例如 12116),_nhandle 是系统定义值(发现其值为 32)。 _nhandle 为什么是 32 ?这个值代表什么意思?(读者思考)

      总之,结论表明在 windows 平台下,对 socket fd 直接进行 read/write 会有错误产生。   没办法还是换成标准的 socket 函数 send/recv 吧。  

结果一切都 ok 了。  

目录
相关文章
|
1月前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
2月前
|
Python
如何在Python中实现线程之间的同步和通信?
【2月更文挑战第17天】【2月更文挑战第51篇】如何在Python中实现线程之间的同步和通信?
|
5月前
|
Java
线程间通信之Object.wait/notify实现
线程间通信之Object.wait/notify实现
33 0
|
5月前
|
消息中间件 存储 Unix
进程间通信和线程间通信总结
写在前面 面试的时候一定不要疲劳战,比如上午面了一个,然后中午不休息直接赶到另外一个相距比较远的公司,影响状态。 面试的时候一定不要紧张,不管对方有几个人,总之面试的时候做好充分准备,休息好,放松心态。 好了,言归正传,开始总结。
42 0
|
4天前
|
存储 NoSQL Redis
深入浅出Redis(二):Redis单线程模型与通信流程
深入浅出Redis(二):Redis单线程模型与通信流程
|
18天前
线程间的通信
线程间的通信
14 0
|
24天前
|
安全 Java 编译器
【JavaEE多线程】线程安全、锁机制及线程间通信
【JavaEE多线程】线程安全、锁机制及线程间通信
33 1
|
1月前
|
Java
Java线程通信的精髓:解析通知等待机制的工作原理
Java线程通信的精髓:解析通知等待机制的工作原理
27 3
Java线程通信的精髓:解析通知等待机制的工作原理
|
1月前
|
存储 缓存 Java
java多线程之线程通信工具类
java多线程之线程通信工具类
|
安全 Java 容器
高并发编程之线程间通信和集合的线程安全
高并发编程之线程间通信和集合的线程安全
24 1