C++ hiredis Redis库是否为异步回调创建自己的线程

C++ hiredis Redis库是否为异步回调创建自己的线程,c++,c,database,redis,C++,C,Database,Redis,我在多线程环境中使用Redis,对它的操作方式有疑问。我在我的C++应用程序中使用了HyReISC C库。p> 我的问题是:如果在触发回调时使用异步模式,回调是否会在Redis客户端创建的另一个线程中处理?与中一样,创建调用的线程是否不受回调处理的影响?谢谢 Redis客户端不创建任何其他clent线程,而是在现有线程中工作 Redis在另一个(主要)过程中工作。您使用的RedisAPI在本地进程中工作,并使用与主进程的进程间通信。异步请求意味着您的进程或线程将任务放到另一个进程或线程,之后可能

我在多线程环境中使用Redis,对它的操作方式有疑问。我在我的C++应用程序中使用了HyReISC C库。p>
我的问题是:如果在触发回调时使用异步模式,回调是否会在Redis客户端创建的另一个线程中处理?与中一样,创建调用的线程是否不受回调处理的影响?谢谢

Redis客户端不创建任何其他clent线程,而是在现有线程中工作

Redis在另一个(主要)过程中工作。您使用的RedisAPI在本地进程中工作,并使用与主进程的进程间通信。异步请求意味着您的进程或线程将任务放到另一个进程或线程,之后可能会执行任何其他yout任务或等待事件。一段时间后,异步回复到达您的应用程序并可供使用。您的应用程序必须使用或任何异步管理系统,该系统通过调用回调来处理事件(在本例中为redis answer)来通知您

异步体系结构意味着运行事件循环,为每个事件调用回调处理程序。调用回调时,可以创建大量异步任务。一旦创建了任务,就应该在任务完成或发生错误时调用回调事件处理程序。可能会将回调分配给启动应用程序或出现新的web连接。调用回调时,可以创建redis任务,稍后将调用结果事件回调。当前线程中的所有内容。您有多个线程,可以合理地预期每个线程有一个事件循环

Redis的单线程特性:

客户端套接字处于非阻塞状态,因为Redis使用多路复用和非阻塞I/O。这意味着您的客户端永远不会被阻塞


Redis中的Redis 2.4线程仅用于在后台执行一些缓慢的I/O操作,主要与磁盘I/O有关,但这并没有改变Redis使用单个线程处理所有请求的事实。

Hiredis在调用RedisAsynchHandlerRead()时调用回调。因此,在您用来调用redisAsyncHandlerRead()的任何线程上都会调用回调

我相信Linux ish C中最小的Redis异步示例如下所示(为了清晰起见,删除了错误检查):

#包括“async.h”
#包括
#包括
void myRedisCallback(redisAsyncContext*c,void*typeless\u reply,void*priv\u数据){
redisReply*r=(redisReply*)无类型回复;
如果(r->type==REDIS\u REPLY\u字符串)
printf(“foo是%s\n”,r->str);
}
int main(){
redisAsyncContext*c=redisAsyncConnect(“本地主机”,6379);
redisAsyncCommand(c,myRedisCallback,NULL,“GET foo”);
对于(int i=0;i<100;i++){
redisAsyncHandleWrite(c);//这将发送命令。
RedisAsyncHandlerRead(c);//如果收到回复,则调用回调。
usleep(10000);//一个真正的应用程序可以在这里做一些事情。
}
返回0;
}
RedisAsyncHandlerRead()或RedisAsyncHandlerWrite()将调用您以前注册的任何回调。它不会每次都“开火”。文档和async.h文件都没有明确说明如何使用文件描述符(ac->c.fd)来实现epoll事件循环:

  #define MAX_EVENTS 10

  int epfd;
  if((epfd = epoll_create1(0)) == -1) {
     // handle error
  };

  redisAsyncContext *ac = redisAsyncConnect("127.0.0.1", 6379);

  if(epoll_ctl(epfd, EPOLL_CTL_ADD, ac->c.fd, &(struct epoll_event) { .events = EPOLLIN | EPOLLOUT | EPOLLET }) == -1) {
     // handle error
  }

  int nfds;
  struct epoll_event events[MAX_EVENTS];
  for(;;) {
     if((nfds = epoll_wait(epfd, events, MAX_EVENTS, -1) == -1) {
       // handle error
     }
     for(int i = 0; i < nfds; i++) {
         if(events[i].events & EPOLLIN) redisAsyncHandleRead(ac);
         if(events[i].events & EPOLLOUT) redisAsyncHandleWrite(ac);
     }
  }
#定义最大事件数10
int epfd;
如果((epfd=epoll_create1(0))=-1){
//处理错误
};
redisAsyncContext*ac=redisAsyncConnect(“127.0.0.1”,6379);
如果(epoll_ctl(epfd,epoll_ctl_ADD,ac->c.fd,&(struct epoll_event){.events=EPOLLIN | epollot | EPOLLET})=-1){
//处理错误
}
int nfds;
结构epoll_事件事件[最大事件];
对于(;;){
if((nfds=epoll\u wait(epfd,events,MAX\u events,-1)=-1){
//处理错误
}
对于(int i=0;i
我了解libuv样式的事件库是如何工作的。这不是我的问题。我的问题是关于redis客户端库的内部工作动态,以及处理回调的位置。必须涉及另一个线程,否则它将被阻塞,除非只有当线程阻塞f时才会发生DB操作或者某种原因。在这种情况下,while(true){}将导致异步操作永远不会发生。我的问题是哪个线程处理callback.Server(main)threan为任务提供服务并向线程发送回复。您的线程未被阻止,因为非阻止套接字用于传输。套接字传输完成后,您可以处理线程中所需的数据。阻止线程的原因是什么?即使使用非阻止套接字,也必须由线程检查套接字状态。如果线程正在进行计算,因此即使套接字是非阻塞的,它也无法处理套接字的调度。此外,库不要求您调用函数来控制诸如libuv(事件库)之类的线程的调度。必须有另一个线程参与其中。不要担心。这是操作系统任务。这是不同的,并且取决于实现。有关更多信息,请参阅:我已经编写了一个epoll服务器,并且了解它的工作原理。epoll生成一个事件列表。必须检查此事件列表,以了解数据何时被删除因为这个检查不是由主线程完成的(没有像libuv那样调用函数来启动它)然后另一个线程必须进行检查。没有其他方法。EPoll仍然需要一个线程不断地检查它。这只是意味着操作系统生成事件,而不是1000个线程每次检查一个套接字。
  #define MAX_EVENTS 10

  int epfd;
  if((epfd = epoll_create1(0)) == -1) {
     // handle error
  };

  redisAsyncContext *ac = redisAsyncConnect("127.0.0.1", 6379);

  if(epoll_ctl(epfd, EPOLL_CTL_ADD, ac->c.fd, &(struct epoll_event) { .events = EPOLLIN | EPOLLOUT | EPOLLET }) == -1) {
     // handle error
  }

  int nfds;
  struct epoll_event events[MAX_EVENTS];
  for(;;) {
     if((nfds = epoll_wait(epfd, events, MAX_EVENTS, -1) == -1) {
       // handle error
     }
     for(int i = 0; i < nfds; i++) {
         if(events[i].events & EPOLLIN) redisAsyncHandleRead(ac);
         if(events[i].events & EPOLLOUT) redisAsyncHandleWrite(ac);
     }
  }