AppDomain.Unload 抛出 ThreadAbortException 异常
AppDomain.Unload throws ThreadAbortException exception
有
public class ObjFromOtherAppDomain : MarshalByRefObject
{
public async void Do(MarshalableCompletionSource<bool> source)
{
await Task.Delay(1000);
source.SetResult(true);
}
}
public class MarshalableCompletionSource<T> : MarshalByRefObject
{
private readonly TaskCompletionSource<T> tsc = new TaskCompletionSource<T>();
public void SetResult(T result) => tsc.SetResult(result);
public void SetException(Exception[] exception) => tsc.SetException(exception);
public void SetCanceled() => tsc.SetCanceled();
public Task<T> Task => tsc.Task;
}
正在做
- 新建
AppDomain
- 在新
AppDomain
中创建 ObjFromOtherAppDomain
的实例
- 调用
Do
方法传递 MarshalableCompletionSource
以便稍后知道 async
Do
方法何时完成。
- 一旦
Do
方法完成,尝试 Unload
AppDomain
public static async Task Main()
{
var otherDomain = AppDomain.CreateDomain("other domain");
var objFromOtherAppDomain = (ObjFromOtherAppDomain)otherDomain
.CreateInstanceAndUnwrap(
typeof(ObjFromOtherAppDomain).Assembly.FullName,
typeof(ObjFromOtherAppDomain).FullName);
var source = new MarshalableCompletionSource<bool>();
objFromOtherAppDomain.Do(source);
await source.Task;
//await Task.Yield();
AppDomain.Unload(otherDomain);
}
得到
System.Threading.ThreadAbortException: 'Thread has aborted. (Exception
from HRESULT: 0x80131530) exception
修复
取消注释 await Task.Yield();
行,Unload
效果很好。
简析
主线程进入 Do
方法并在行 await Task.Delay(1000)
上,主线程 returns 返回 Main
方法,同时从 [=28 拉出新的后台线程=](它发生在 otherDomain
中)并继续执行 continuation,在这种情况下,Do
方法的其余部分。
之后,相同(后台)线程开始执行Main
方法的其余部分(await source.Task
之后的部分)
在那一刻后台线程命中 AppDomain.Unload(otherDomain)
,它应该在 otherDomain
中完成并愉快地卸载它,但显然不是。
如果我让出(释放,释放)那个背景线程await Task.Yield()
,新的背景线程开始发挥作用并愉快地AppDomain.Unload
.
这是为什么?
在同事的帮助下,我发现了问题。
短
当结果设置为 TaskCompletionSource
时,附加到 TaskCompletionSource.Task
的延续在调用 TaskCompletionSource.SetResult
的 相同 线程上运行,导致 Do
调用 AppDomain.Unload
时未完成的方法。
详细
Main
-> objFromOtherAppDomain.Do(source);
- Thread1
开始执行 Do
方法
Do
-> await Task.Delay(1000);
- Thread1
return 返回 Main
方法并等待 source.Task
, Thread2
继续执行 Do
方法。
Do
-> source.SetResult(true);
- Thread2
将结果设置为 MarshalableCompletionSource.Task
并且 继续执行 Main 方法(没有完成Do
方法)
Main
-> AppDomain.Unload(otherDomain);
- Thread2
尝试卸载 AppDomain
,但由于 Do
方法尚未完成完成,卸载失败。
另一方面,如果我们做await Task.Yield()
,它会导致Thread2
到return从Main
方法到Do
方法并完成它up,之后AppDomain
就可以卸载了
相关
Calling TaskCompletionSource.SetResult in a non blocking manner
有
public class ObjFromOtherAppDomain : MarshalByRefObject
{
public async void Do(MarshalableCompletionSource<bool> source)
{
await Task.Delay(1000);
source.SetResult(true);
}
}
public class MarshalableCompletionSource<T> : MarshalByRefObject
{
private readonly TaskCompletionSource<T> tsc = new TaskCompletionSource<T>();
public void SetResult(T result) => tsc.SetResult(result);
public void SetException(Exception[] exception) => tsc.SetException(exception);
public void SetCanceled() => tsc.SetCanceled();
public Task<T> Task => tsc.Task;
}
正在做
- 新建
AppDomain
- 在新
AppDomain
中创建 - 调用
Do
方法传递MarshalableCompletionSource
以便稍后知道async
Do
方法何时完成。 - 一旦
Do
方法完成,尝试Unload
AppDomain
ObjFromOtherAppDomain
的实例
public static async Task Main()
{
var otherDomain = AppDomain.CreateDomain("other domain");
var objFromOtherAppDomain = (ObjFromOtherAppDomain)otherDomain
.CreateInstanceAndUnwrap(
typeof(ObjFromOtherAppDomain).Assembly.FullName,
typeof(ObjFromOtherAppDomain).FullName);
var source = new MarshalableCompletionSource<bool>();
objFromOtherAppDomain.Do(source);
await source.Task;
//await Task.Yield();
AppDomain.Unload(otherDomain);
}
得到
System.Threading.ThreadAbortException: 'Thread has aborted. (Exception from HRESULT: 0x80131530) exception
修复
取消注释 await Task.Yield();
行,Unload
效果很好。
简析
主线程进入 Do
方法并在行 await Task.Delay(1000)
上,主线程 returns 返回 Main
方法,同时从 [=28 拉出新的后台线程=](它发生在 otherDomain
中)并继续执行 continuation,在这种情况下,Do
方法的其余部分。
之后,相同(后台)线程开始执行Main
方法的其余部分(await source.Task
之后的部分)
在那一刻后台线程命中 AppDomain.Unload(otherDomain)
,它应该在 otherDomain
中完成并愉快地卸载它,但显然不是。
如果我让出(释放,释放)那个背景线程await Task.Yield()
,新的背景线程开始发挥作用并愉快地AppDomain.Unload
.
这是为什么?
在同事的帮助下,我发现了问题。
短
当结果设置为 TaskCompletionSource
时,附加到 TaskCompletionSource.Task
的延续在调用 TaskCompletionSource.SetResult
的 相同 线程上运行,导致 Do
调用 AppDomain.Unload
时未完成的方法。
详细
Main
->objFromOtherAppDomain.Do(source);
-Thread1
开始执行Do
方法Do
->await Task.Delay(1000);
-Thread1
return 返回Main
方法并等待source.Task
,Thread2
继续执行Do
方法。Do
->source.SetResult(true);
-Thread2
将结果设置为MarshalableCompletionSource.Task
并且 继续执行 Main 方法(没有完成Do
方法)Main
->AppDomain.Unload(otherDomain);
-Thread2
尝试卸载AppDomain
,但由于Do
方法尚未完成完成,卸载失败。
另一方面,如果我们做await Task.Yield()
,它会导致Thread2
到return从Main
方法到Do
方法并完成它up,之后AppDomain
就可以卸载了
相关
Calling TaskCompletionSource.SetResult in a non blocking manner