同步(Synchronous)
异步( Asynchronous)
阻塞( Blocking )
非阻塞( Nonblocking)
同步异步 指的是在客户端
同步意味着 客户端提出了一个请求以后,在回应之前只能等待
异步意味着 客户端提出一个请求以后,还可以继续提其他请求
阻塞非阻塞 指的是服务器端
阻塞意味着 服务器接受一个请求后,在返回结果以前不能接受其他请求
非阻塞意味着 服务器接受一个请求后,尽管没有返回结果,还是可以继续接受其他请求
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由调用者主动等待这个调用的结果。
而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。
举个通俗的例子:
同步通信机制
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
异步通信机制
书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
还是上面的例子,
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
网上同步与异步比较经典的例子
static void Main(string[] args)
{
Console.WriteLine("********同步调用开始**********");
int result = Add(1, 2);
Console.WriteLine("同步调用完毕,执行结果为:" + result);
Console.WriteLine("********异步调用开始**********");
SynAdd(1, 2, (r) => {
Console.WriteLine("异步调用完毕,执行结果为:" + r);
});
Console.WriteLine("-------完毕!----------");
Console.ReadLine();
}
///
/// 同步方法
///
///
///
///
static int Add(int a, int b)
{
Thread.Sleep(5000);
return a + b;
}
///
/// 异步调用
///
///
///
/// 委托对象
static void SynAdd(int a, int b, Action<int> callback)
{
Func<int> func = () =>
{
Thread.Sleep(5000);
return a + b;
};//声明异步方法实现方式
func.BeginInvoke((ar) =>
{
var result = func.EndInvoke(ar);//调用完毕执行的结果
callback.Invoke(result);//委托执行,回传结果值
}, null);
}
static void Main(string[] args)
{
//第一种
var task = new Task(() =>
{
Console.WriteLine("任务开始....");
Console.WriteLine("任务结束....");
});
task.Start();
Console.WriteLine("主线程开始....");
Console.WriteLine("主线程结束....");
//第二种
new Task(() =>
{
Console.WriteLine("任务开始2....");
Console.WriteLine("任务结束2....");
}).Start();
Console.WriteLine("主线程开始2....");
Console.WriteLine("主线程结束2....");
//第三种
new Task((i)=> {
Console.WriteLine("任务开始3....");
for(int j = 0;j<(int)i;j++)
{
Console.WriteLine(j.ToString());
}
Console.WriteLine("任务结束3....");
},10).Start();
Console.WriteLine("主线程开始3....");
Console.WriteLine("主线程结束3....");
//第四种
new Task(StartCode,10).Start();
Console.WriteLine("主线程开始4....");
Console.WriteLine("主线程结束4....");
Console.ReadLine();
}
private static void StartCode(object i)//
{
Console.WriteLine("任务开始4....");
Console.WriteLine("开始执行4子线程...{0}",i);
Console.WriteLine("任务结束4....");
}
Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制。可以认为 Task.Run 是简化的 Task.Factory.StartNew 的使用,除了需要指定一个线程是长时间占用的,否则就使用 Task.Run。
static void Main(string[] args)
{
//第一种
var task = new Task( () =>
{
Console.WriteLine("任务开始....");
Thread.Sleep(100);
Console.WriteLine("任务结束....");
});
task.Start();
Console.WriteLine("主线程开始....");
Console.WriteLine("主线程结束....");
//第二种
Task.Run(() => {
Console.WriteLine("任务开始2....");
Console.WriteLine("任务结束2....");
});
Console.WriteLine("主线程开始2....");
Console.WriteLine("主线程结束2....");
Task.Factory.StartNew(() =>
{
try//异常是在里面抛出的否则就是错误写法没有作用。
{
Console.WriteLine("进行 线程" + Thread.CurrentThread.ManagedThreadId);
}
catch
{
throw new Exception("抛出异常");
}
}, TaskCreationOptions.LongRunning);//长时间运行
Console.Read();
}
Task.Wait(参数):
参数=空: 无限等待下去,知道该线程结束
参数=millisecondTimeout; 等待指定的时间)
//第一种
var task = new Task( () =>
{
Console.WriteLine("任务开始....");
Thread.Sleep(100);
Console.WriteLine("任务结束....");
});
task.Start();
Console.WriteLine("主线程开始....");
Console.WriteLine("主线程结束....");
//第二种
var task2 = new Task(() =>
{
Console.WriteLine("任务开始2....");
Thread.Sleep(100);
Console.WriteLine("任务结束2....");
});
task2.Start();
Console.WriteLine("主线程开始2....");
task2.Wait(10);//阻塞时间 不用等其它完成,只等时间
Console.WriteLine("主线程结束2....");
//第三种
var task3 = new Task(() =>
{
Console.WriteLine("任务开始3....");
Thread.Sleep(100);
Console.WriteLine("任务结束3....");
});
task3.Start();
Console.WriteLine("主线程开始3....");
task3.Wait();//这里相当于阻塞,要等其它完成才行
Console.WriteLine("主线程结束3....");
结果
Task.waitall 和WaitAny参数而是一个常规方法体
static void Main(string[] args)
{
//第一种
var task = new Task( () =>
{
Console.WriteLine("任务开始....");
Thread.Sleep(100);
Console.WriteLine("任务结束....");
});
task.Start();
Console.WriteLine("主线程开始....");
Console.WriteLine("主线程结束....");
//第二种
var task2 = new Task(() =>
{
Console.WriteLine("任务开始2....");
Thread.Sleep(100);
Console.WriteLine("任务结束2....");
});
task2.Start();
Console.WriteLine("主线程开始2....");
Console.WriteLine("主线程结束2....");
//第三种
var task3 = new Task(() =>
{
Console.WriteLine("任务开始3....");
Thread.Sleep(100);
Console.WriteLine("任务结束3....");
});
task3.Start();
Console.WriteLine("主线程开始3....");
Console.WriteLine("主线程结束3....");
Task.WaitAny(task, task2, task3);
Console.WriteLine("WaitAny");
Task.WaitAll(task, task2, task3);
Console.WriteLine("WaitAll");
Console.Read();
}
结果
Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。可以把前面的sleep改成task。delay。。。。