前言
在实际应用中,很多函数需要进行长时间的计算、I/O操作或等待其他进程/线程处理结果,而这些操作往往会造成程序的阻塞或耗时较长的等待,影响程序的效率和用户体验。如果使用同步调用方式,调用者必须一直阻塞等待函数返回结果,这种方式会浪费处理器时间,降低程序的效率。而异步调用则可以使得调用者无需阻塞,继续执行其他任务,当函数执行结束后再通过回调函数将结果返回给调用者,提高了程序的运行效率和并发性能。
另外,在处理多进程/线程协作中,同步调用往往可控性更强,代码更易于理解和维护。一些函数在执行过程中需要保证原子性和互斥性,通常需要使用同步机制来实现,以避免数据损坏和竞态条件等问题。对于这种情况,同步调用是较好的选择,确保了执行的正确性和稳定性。
所以首先我们要理解什么是同步调用和异步调用?
含义
在通信、网络编程等领域,同步调用通常指程序在调用某个函数时会一直等待该函数执行完成并返回结果后才继续往下执行,因此该函数调用过程中会阻塞当前线程。而异步调用则是指程序在调用某个函数时不需要等待该函数执行完成,而是立即返回一个状态或者回调函数,由该函数自行在后台执行,因此不会阻塞当前线程。举个例子,可能会更好理解一点。
举例说明
假设需要从标准输入读取一行字符串并输出到标准输出,这时可以使用fgets函数进行同步调用,或使用非阻塞I/O函数进行异步调用。
- 同步调用
#include <stdio.h>
#define MAX_LEN 128
int main() {
char buf[MAX_LEN];
fgets(buf, MAX_LEN, stdin);
printf("%s", buf);
}
在这个例子中,调用fgets函数会一直等待用户在标准输入上输入一行数据后才返回字符串,因为它是同步调用。如果没有输入,程序就一直阻塞在fgets函数上。
- 异步调用
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define MAX_LEN 128
int main() {
char buf[MAX_LEN];
int fd = open("/dev/stdin", O_RDONLY | O_NONBLOCK);
while(1) {
ssize_t n = read(fd, buf, MAX_LEN);
if (n > 0) {
write(STDOUT_FILENO, buf, n);
break;
}
}
close(fd);
}
在这个例子中,我们使用了open函数打开/dev/stdin文件,并设置O_NONBLOCK选项使其成为非阻塞I/O文件。然后我们使用read函数进行异步读取,并检查是否有数据可用。如果存在,则使用write函数将其输出到标准输出,并退出循环。由于我们使用了非阻塞的I/O模式,因此程序不会阻塞在read函数上,可以继续执行其他命令。
总结:如何判断同步调用和异步调用?
- 判断函数是否立即返回。如果函数在调用时可以立即返回,并且不阻塞当前线程/进程,则该函数可能是异步调用。反之,如果函数需要等待某些操作完成后才能返回,并且阻塞了当前线程/进程,则这个函数很可能是同步调用。
- 判断函数执行结果的获取方式。对于同步调用,一般都是在函数返回后立即获取函数执行结果;而对于异步调用,则大部分情况下需要通过回调函数或者其他机制来获取函数执行的结果。