楼主看了一下B站视频:https://www.bilibili.com/video/BV1b5411i7VX/?spm_id_from=333.788&vd_source=f2e80a31de0479ff169b9ddd0c34f7f8 对同步中使用异步进行了学习,但是学习过程中发现,该视频的21分钟(大致代码见下方)处如果将Thread.Sleep(2000) 延长至5000等更长的时间,最后的 Console.WriteLine($"Data is Loaded: {myDataModel.IsDataLoaded}."); 就不会触发,该视频下方也有用户问到,但是暂时没有回答,故来求助。
具体问题以及简单说明过程:主线程中声明一个 MyDataModel,在 MyDataModel 中构造函数中会触发 SafeFireAndForget 方法,此方法调用 LoadDataAsync 异步方法,如果触发成功就会启动 onCompleted 方法,否则启动 onError ,尝试通过这样的方式避免 async void 不会抛出错误且会导致线程崩溃的问题。但是在LoadData 中真的抛出错误时,该代码最终能否执行主线程中 Thread.Sleep 后续的代码取决于 Sleep 的时间,这是为什么呢?
楼主猜想是Sleep 时间过长导致 async void 抛出的错误导致所处线程崩溃,此时主线程还活着,但是主线程 Sleep 一段时间后会知道有一处线程崩溃而导致自身崩溃,如果Sleep不太长,主线程Sleep结束后就会继续执行后续代码,但是如果过长主线程自身崩溃时,都没Sleep结束,更不可能执行后续代码了。由于 LoadData 只delay了 1s,但是主线程睡了 2 s却还是可以完成后续代码,明明Sleep时间内LoadData 就抛出异常了主线程却能执行完,故有所疑问,也不知道猜想对不对。
public static async void AsyncM()
{
MyDataModel myDataModel = new MyDataModel();
Console.WriteLine("Loading Data.");
Thread.Sleep(2000);
List<int>? data = myDataModel.Data;
Console.WriteLine($"Data is Loaded: {myDataModel.IsDataLoaded}.");
}
class MyDataModel
{
public List<int>? Data { get; private set; }
public bool IsDataLoaded { get; private set; } = false;
public MyDataModel() {
SafeFireAndForget(LoadDataAsync(), () => IsDataLoaded = true, e=> throw e);
}
static async void SafeFireAndForget(Task task, Action? onCompleted = null, Action<Exception>? onError = null)
{
try
{
await task;
onCompleted?.Invoke();
}
catch (Exception e)
{
onError?.Invoke(e);
}
}
async Task LoadDataAsync()
{
await Task.Delay(1000);
throw new Exception();
//Data = Enumerable.Range(1,10).ToList();
}
}
总而言之希望有个大佬能分享一下这个 async void 具体问题。
具体问题以及简单说明过程:主线程中声明一个 MyDataModel,在 MyDataModel 中构造函数中会触发 SafeFireAndForget 方法,此方法调用 LoadDataAsync 异步方法,如果触发成功就会启动 onCompleted 方法,否则启动 onError ,尝试通过这样的方式避免 async void 不会抛出错误且会导致线程崩溃的问题。但是在LoadData 中真的抛出错误时,该代码最终能否执行主线程中 Thread.Sleep 后续的代码取决于 Sleep 的时间,这是为什么呢?
楼主猜想是Sleep 时间过长导致 async void 抛出的错误导致所处线程崩溃,此时主线程还活着,但是主线程 Sleep 一段时间后会知道有一处线程崩溃而导致自身崩溃,如果Sleep不太长,主线程Sleep结束后就会继续执行后续代码,但是如果过长主线程自身崩溃时,都没Sleep结束,更不可能执行后续代码了。由于 LoadData 只delay了 1s,但是主线程睡了 2 s却还是可以完成后续代码,明明Sleep时间内LoadData 就抛出异常了主线程却能执行完,故有所疑问,也不知道猜想对不对。
public static async void AsyncM()
{
MyDataModel myDataModel = new MyDataModel();
Console.WriteLine("Loading Data.");
Thread.Sleep(2000);
List<int>? data = myDataModel.Data;
Console.WriteLine($"Data is Loaded: {myDataModel.IsDataLoaded}.");
}
class MyDataModel
{
public List<int>? Data { get; private set; }
public bool IsDataLoaded { get; private set; } = false;
public MyDataModel() {
SafeFireAndForget(LoadDataAsync(), () => IsDataLoaded = true, e=> throw e);
}
static async void SafeFireAndForget(Task task, Action? onCompleted = null, Action<Exception>? onError = null)
{
try
{
await task;
onCompleted?.Invoke();
}
catch (Exception e)
{
onError?.Invoke(e);
}
}
async Task LoadDataAsync()
{
await Task.Delay(1000);
throw new Exception();
//Data = Enumerable.Range(1,10).ToList();
}
}
总而言之希望有个大佬能分享一下这个 async void 具体问题。