获取到异步函数的结果再执行后续代码_同步请求和异步请求

同步请求和异步请求的区别

  • 同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
  • 异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。

即同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。

所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

异步的概念和同步相对。当一个同步调用发出后,调用者要一直等待返回消息(结果)通知后,才能进行后续的执行;当一个异步过程调用发出后,调用者不能立刻得到返回消息(结果)。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

针对以上描述,实际工程编程中,具体我们如何根据场景选择和使用同步和异步:

异步的使用场景:

  • 不涉及共享资源,或对共享资源只读,即非互斥操作
  • 没有时序上的严格关系
  • 不需要原子操作,或可以通过其他方式控制原子性
  • 常用于IO操作等耗时操作,因为比较影响客户体验和使用性能
  • 不影响主线程逻辑

同步的使用场景:不使用异步的时候

同步的好处:

  • 同步流程对结果处理通常更为简单,可以就近处理。
  • 同步流程对结果的处理始终和前文保持在一个上下文内。
  • 同步流程可以很容易捕获、处理异常。
  • 同步流程是最天然的控制过程顺序执行的方式。

异步的好处:

  • 异步流程可以立即给调用方返回初步的结果。
  • 异步流程可以延迟给调用方最终的结果数据,在此期间可以做更多额外的工作,例如结果记录等等。
  • 异步流程在执行的过程中,可以释放占用的线程等资源,避免阻塞,等到结果产生再重新获取线程处理。
  • 异步流程可以等多次调用的结果出来后,再统一返回一次结果集合,提高响应效率。

接下来基于http请求分别实现同步和异步获取天气信息的编程:

贴出来一个http请求格式,代码需要按照这个格式拼接相关的字符串。

88fd49e4aee4e58de5234a03a16d5f41.png

一个获取天气信息的网站API说明:https://docs.seniverse.com/api/weather/now.html

3ae69e8f2fddf53bec2f7c4f9ed69b3e.png

http请求sync的代码实现:

#include #include #include #include #include #include #include #include #include #include #include #include #include /* close */#include  #include #define HTTP_VERSION    "HTTP/1.1"#define USER_AGENT"User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2"#define ENCODE_TYPE"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"//设置连接类型,由于是短连接,不需要设置为keep-alive#define CONNECTION_TYPE "Connection: close"#define BUFFER_SIZE4096struct http_request {char *hostname;char *resource;};struct http_request reqs[] = {{"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=beijing&language=zh-Hans&unit=c" },{"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=shenzhen&language=zh-Hans&unit=c" },};char *host_to_ip(const char *hostname) {struct hostent *host_entry = gethostbyname(hostname);  /*******************************************************************************************************  由于gethostbyname这个函数不可重入,并不适用于多线程环境以及其它对DNS解析速度要求较高的程序  后来又增加一个函数gethostbyname_r,解决gethostbyname的问题,但是又由于该函数对ipv6的支持不好,  最后目前推荐使用getaddrinfo  *********************************************************************************************************/  if (host_entry) {return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);} else {return NULL;}}int http_create_socket( char *ip) {int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in sin = {0};sin.sin_addr.s_addr = inet_addr(ip);sin.sin_port = htons(80);sin.sin_family = AF_INET;if (-1 == connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {return -1;}fcntl(sockfd, F_SETFL, O_NONBLOCK);return sockfd;}char *http_send_request(int sockfd, const char *hostname, const char *resource) {char buffer[BUFFER_SIZE] = {0};int len = sprintf(buffer, "GET %s %sHost: %s%s", resource, HTTP_VERSION, hostname, CONNECTION_TYPE );//拼接http请求send(sockfd, buffer, strlen(buffer), 0);struct timeval tv;tv.tv_sec = 5;tv.tv_usec = 0;fd_set fdread;FD_ZERO(&fdread);FD_SET(sockfd, &fdread);char *result = malloc(sizeof(int));result[0] = '0';while (1) {int selection = select(sockfd+1, &fdread, NULL, NULL, &tv);if (!selection || !(FD_ISSET(sockfd, &fdread))) {break;} else {len = recv(sockfd, buffer, BUFFER_SIZE, 0);if (len == 0) break;result = realloc(result, (strlen(result) + len + 1) * sizeof(char));strncat(result, buffer, len);}}return result;}int http_sync_client_commit(const char *hostname, const char *resource) {char *ip = host_to_ip(hostname);//根据域名获取IP地址int sockfd = http_create_socket(ip);//创建socket,并使用select监听查询fdchar *content =  http_send_request(sockfd, hostname, resource);//发送请求if (content == NULL) {printf("have no data");}puts(content);close(sockfd);free(content);}int main(int argc, char *argv[]) {int count = sizeof(reqs) / sizeof(reqs[0]);int i = 0;for (i = 0;i < count;i ++) {http_client_commit(reqs[i].hostname, reqs[i].resource);}

运行结果:

6367cf86b45662cb268af0100823f065.png

http请求async的代码实现:

#include #include #include #include #include #include #include #include #include #include #include #include #include /* close */#include  #include #define HTTP_VERSION    "HTTP/1.1"#define USER_AGENT"User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2"#define ENCODE_TYPE"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"#define CONNECTION_TYPE "Connection: close"#define BUFFER_SIZE4096#define ASYNC_CLIENT_NUM1024#define HOSTNAME_LENGTH128typedef void (*async_result_cb)(const char *hostname, const char *result);struct ep_arg {int sockfd;char hostname[HOSTNAME_LENGTH];async_result_cb cb;};struct async_context {int epfd;pthread_t thread_id;};struct http_request {char *hostname;char *resource;};struct http_request reqs[] = {{"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=beijing&language=zh-Hans&unit=c" },{"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=shenzhen&language=zh-Hans&unit=c" },};char *host_to_ip(const char *hostname) {struct hostent *host_entry = gethostbyname(hostname);if (host_entry) {return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);} else {return NULL;}}int http_async_client_commit(struct async_context *ctx, const char *hostname, const char *resource, async_result_cb cb) {char *ip = host_to_ip(hostname);if (ip == NULL) return -1;int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in sin = {0};sin.sin_addr.s_addr = inet_addr(ip);sin.sin_port = htons(80);sin.sin_family = AF_INET;if (-1 == connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {return -1;}fcntl(sockfd, F_SETFL, O_NONBLOCK);char buffer[BUFFER_SIZE] = {0};int len = sprintf(buffer, "GET %s %sHost: %s%s", resource, HTTP_VERSION, hostname, CONNECTION_TYPE );//拼接http请求int slen = send(sockfd, buffer, strlen(buffer), 0);//发送请求struct ep_arg *eparg = (struct ep_arg*)calloc(1, sizeof(struct ep_arg));//创建一个结构体,把fd和回调函数放进去,方便epoll返回的时候,就可以取到对应fd和回调函数,用于客户端同一个线程处理数据if (eparg == NULL) return -1;eparg->sockfd = sockfd;eparg->cb = cb;struct epoll_event ev;ev.data.ptr = eparg;//传递参数给epollev.events = EPOLLIN;int ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev); //把http请求的fd加入到epoll的事件注册函数return ret;}int http_async_client_uninit(struct async_context *ctx) {close(ctx->epfd);pthread_cancel(ctx->thread_id);return 0;}static void *http_async_client_callback(void *arg) {struct async_context *ctx = (struct async_context*)arg;int epfd = ctx->epfd;while (1) {struct epoll_event events[ASYNC_CLIENT_NUM] = {0};int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -1);//等待准备就绪的fdif (nready < 0) {if (errno == EINTR || errno == EAGAIN) {continue;} else {break;}} else if (nready == 0) {continue;}printf("nready:%d", nready);int i = 0;for (i = 0;i < nready;i ++) {struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;//http请求的fd加入epoll的事件注册函数时候ev.data.ptr = eparg,现在通过ptr获取当时传递的参数int sockfd = data->sockfd;char buffer[BUFFER_SIZE] = {0};struct sockaddr_in addr;size_t addr_len = sizeof(struct sockaddr_in);int n = recv(sockfd, buffer, BUFFER_SIZE, 0);//读取数据data->cb(data->hostname, buffer); //调用回调,处理返回的数据int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);//完成之后需要移除相应的fd//printf("epoll_ctl DEL --> sockfd:%d", sockfd);close(sockfd); /free(data);}}}struct async_context *http_async_client_init(void) {int epfd = epoll_create(1); // 创建一个epoll的句柄if (epfd < 0) return NULL;struct async_context *ctx = calloc(1, sizeof(struct async_context));if (ctx == NULL) {close(epfd);return NULL;}ctx->epfd = epfd;int ret = pthread_create(&ctx->thread_id, NULL, http_async_client_callback, ctx);//创建线程,注册回调用于异步epoll fd events的监听查询if (ret) {perror("pthread_create");return NULL;}usleep(1); return ctx;}static void http_async_client_result_callback(const char *hostname, const char *result) {printf("hostname:%s, result:%s", hostname, result);}int main(int argc, char *argv[]) {struct async_context *ctx = http_async_client_init();//创建epoll和一个线程用于异步处理if (ctx == NULL) return -1;int count = sizeof(reqs) / sizeof(reqs[0]);int i = 0;for (i = 0;i < count;i ++) {http_async_client_commit(ctx, reqs[i].hostname, reqs[i].resource, http_async_client_result_callback);}getchar();}

运行结果:

a9bdbbf95cc40d6d49180c9e901c4933.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值