异步接收子线程的返回值

上次在做版本升级时遇到一个这样的需求:服务器需要向客户端发送版本文件,采用的是TCP + pthread来实现,其中,发送结果需要返回给主线程。这让我想起了子进程的操作退出后会给主进程发送一个信号,而主进程会在收到信号后调用WaitPid来获取返回值。而在线程里面退出就没有类似的信号发送出来,刚开始感觉无从下手,后面浏览了libevent代码后便有有了灵感,使用eventfd可以解决这个问题。


/* event fd, use for thread communicate */
//首先创建一个eventfd
ThreadNotifyFD = eventfd(0, 0);

//加入异步读操作
gevent.thread_ev = event_new(gevent.base, ThreadNotifyFD, EV_READ|EV_PERSIST, 
	do_thread_notify, (void*)base);
event_add(gevent.thread_ev, NULL);

void do_thread_notify(evutil_socket_t fd, short event, void *arg) 
{
	pthread_t thread_id;
	struct thread_args *threadarg;

	ev_uint64_t msg;
	ev_ssize_t r;
	
	r = read(fd, (void*) &msg, sizeof(msg));
	if (r < 0 && errno != EAGAIN) {
		log_debug("do thread notify read failed: %s\n", strerror(errno));
		trackExit(EXIT_READ);
		return;
	}

	//收到子线程的返回值
	if (pthread_join(msg, (void*)&threadarg)!=0 || !threadarg) {
		log_debug("pthread join status failed: %s, %x\n
			", strerror(errno), threadarg);
		trackExit(EXIT_THREAD);
		return;
	}

	log_debug("arg.peer: %s, writelen: %d, status: %d\n", 
		inet_ntoa(threadarg->addr), threadarg->write_len, threadarg->status);

	if (threadarg->status == 1) {
		mark_peer_flags(threadarg->addr, BIN_SENDED);
	}

	free(threadarg);
	return;
}

void do_filetranst(evutil_socket_t fd, short event, void *arg) 
{
	struct sockaddr_in peeraddr;
	pthread_t thread_id;
	int  socklen;
	int s;

	fd = accept(fd, (struct sockaddr*)&peeraddr, &socklen);
	if (fd < 0) {
		log_debug("accept failed: %s\n", strerror(errno));
		return;
	}

	if (!IamDrWithBin || !find_peer(peeraddr.sin_addr)) {
		log_debug("bin not ready or accept unknown peer: %s, %d\n
			", inet_ntoa(peeraddr.sin_addr), IamDrWithBin);
		return ;
	}

	log_debug("accept non-dr %s\n", inet_ntoa(peeraddr.sin_addr));

	//客户端连接来了之后分配一个子线程进程处理
	s = pthread_create(&thread_id, NULL, (void *)filetranst_thread, (void *)fd);
	if (s != 0) {

		log_debug("pthread create failed: %s\n", strerror(errno));
		trackExit(EXIT_THREAD);
	}
	
	return;
}

void *filetranst_thread(void *arg)
{
	struct sockaddr_in peeraddr;
	struct thread_args *args;
	unsigned int peerlen = sizeof(struct sockaddr_in);
	struct stat file_info;
	int datafd, data_size;
	int fd = (int)arg;
	int rc;

	ev_uint64_t msg = pthread_self();
	
	rc = getpeername (fd, (struct sockaddr *)&peeraddr, &peerlen);
	if (rc < 0) {
		log_debug("getpeername failed: %s\n", strerror(errno));
		pthread_detach(pthread_self());/* 自生自灭 */
		return;
	}

	log_debug("start to transmit file to peer:%s, 
		port:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

	datafd = open("/tmp/firmware.img", O_RDONLY);
	if (datafd < 0 || fstat(datafd, &file_info) < 0) {
		log_debug("open file firmware.img failed: %s\n", strerror(errno));
		trackExit(EXIT_OPEN);
		goto fin;
	}
		
	log_debug("file size: %u\n", file_info.st_size);
	
	rc = sendfile(fd, datafd, NULL, file_info.st_size);
	if (rc < 0) {
		log_debug("sendfile failed: %s\n", strerror(errno));
	}
	else
		log_debug("send %u bytes finished\n", rc);


fin:	
	
	close(datafd);
	close(fd);
	
	args = (struct thread_args *)malloc(sizeof(struct thread_args));
	if (!arg) {
		log_debug("malloc args failed\n");
		trackExit(EXIT_MEMORY);
	} else {
		args->addr = peeraddr.sin_addr;
		args->write_len = rc;
		args->status = (rc == file_info.st_size);
	}
	
	do {
		//子线程操作完毕后就把相应的结果打包结构体返回,
		//并通知eventfd,在主线程内会调用do_thread_notify执行pthread_join返回结果
		rc = write(ThreadNotifyFD, (void*) &msg, sizeof(msg));
	} while (rc < 0 && errno == EAGAIN);

	pthread_exit((void*)args);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值