返回目录

关于死锁的原因

理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余的代码。这个context就是当前的SynchronizationContext ,除非它是空的。WEB应用程序的SynchronizationContext 有排他性,只允许一个线程运行。当await 完成的时候,它试图在它原来的代码上下文执行它剩余的部分,但是该代码上下文中已经有一个线程在了,就是那个一直在同步等待async 完成的那个线程,它们两个相互等待,因此就死锁了。

MSDN使用异步应该注意

规则
描述
例外
避免使用 async void
优先使用 async Task 而不用 async void
Event handlers
Async到顶
不要混合使用 blocking 和 async 的代码
Console main method
注意配置好执行的context
尽量设置 ConfigureAwait(false)
需要context的除外

感恩的心

多谢网上很多文章的分享,在相关文章中找到了在同步代码中使用异步代码的无阻塞方案,之前也自己写了几个测试的DEMO,但Task<T>这种带有返回值的异步方法还是会出现死锁,之前代码如下:

    /// <summary>
/// 大叔测试
/// </summary>
public class tools
{
#region 假设这些方法被第三方被封装的,不可修改的方法
public static async Task TestAsync()
{
await Task.Delay()
.ConfigureAwait(false);//不会死锁
} public static async Task<string> GetStrAsync()
{
return await Task.Run(() => "OK").ConfigureAwait(false);
} public static async Task DelayTestAsync()
{
Logger.LoggerFactory.Instance.Logger_Info("DelayAsync");
await Task.Delay();
} public static async Task<string> DelayGetStrAsync()
{
return await Task.Run(() => "OK");
}
#endregion #region 我们需要在自己代码中封装它,解决线程死锁
/// <summary>
/// 没有返回值的同步调用异步的实体
/// </summary>
/// <param name="func"></param>
public static void ForWait(Func<Task> func)
{
func().ConfigureAwait(false);
}
/// <summary>
/// 存在返回值的同步调用异步的实体
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
public static T ForResult<T>(Func<Task<T>> func)
{
var a = func();
a.ConfigureAwait(false);
return a.Result;
}
#endregion
}

对于上面的代码,当执行一个Task反回类型(即无返回结果)时,程序是没有问题的,但可以存在返回结果,那么上面的ForResult方法依旧会产生死锁!执着的我当然不会就此罢休,找了一些文章后,终于还是有了结果,在对当前上下文和异步上下文进行了简

单的处理后,最终还是实现了同步与异步的并存所以说,人是最聪明的一种动物,一切都皆有可能,只要你想!

Lind.DDD.Utils.AsyncTaskManager代码如下,希望可以给大家带来一些启发和帮助

  /// <summary>
/// 异步线程管理-在同步程序中调用异步,解决了线程死锁问题
/// </summary>
public class AsyncTaskManager
{
/// <summary>
/// 运行无返回类型的异步方法
/// </summary>
/// <param name="task"></param>
public static void RunSync(Func<Task> task)
{
var oldContext = SynchronizationContext.Current;//同步上下文
var synch = new ExclusiveSynchronizationContext();//异步上下文
SynchronizationContext.SetSynchronizationContext(synch);//设置当前同步上下文
synch.Post(async obj =>
{
try
{
await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// 运行返回类型为T的异步方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="task"></param>
/// <returns></returns>
public static T RunSync<T>(Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
T ret = default(T);//动作的默认值
synch.Post(async obj =>
{
try
{
ret = await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return ret;
} /// <summary>
/// 异步上下文对象
/// </summary>
class ExclusiveSynchronizationContext : SynchronizationContext
{
private bool done;
public Exception InnerException { get; set; }
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>(); public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("We cannot send to our same thread");
}
/// <summary>
/// 添加到异步队列
/// </summary>
/// <param name="d"></param>
/// <param name="state"></param>
public override void Post(SendOrPostCallback d, object state)
{
lock (items)
{
items.Enqueue(Tuple.Create(d, state));
}
workItemsWaiting.Set();
}
/// <summary>
/// 异步结束
/// </summary>
public void EndMessageLoop()
{
Post(obj => done = true, null);
}
/// <summary>
/// 处理异步队列中的消息
/// </summary>
public void BeginMessageLoop()
{
while (!done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (items)
{
if (items.Count > )
{
task = items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncInline.Run method threw an exception.",
InnerException);
}
}
else
{
workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}

最后我们进行测试中,看到线程没有出现死锁问题!

感谢各位的阅读!

返回目录

C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决的更多相关文章

  1. C#~异步编程再续~await与async引起的w3wp.exe崩溃

    返回目录 最近怪事又开始发生了,IIS的应用程序池无做挂掉,都指向同一个矛头,async,threadPool,Task,还有一个System.NullReferenceException,所以这些都 ...

  2. C#中的异步编程--探索await与async关键字的奥妙之处,原来理解和使用异步编程可以这么简单

    前言 await与async是C#5.0推出的新语法,关于await与async有很多文章讲解.但看完后有没有这样一种感觉,感觉这东西像是不错,但好像就是看不太懂,也不清楚该怎么使用.虽然偶有接触,但 ...

  3. 异步编程,await async入门

    网上很多异步编程的文章,提供一篇入门: 异步编程模型 .net支持3种异步编程模式: msdn:https://docs.microsoft.com/zh-cn/dotnet/standard/asy ...

  4. C#进阶——从应用上理解异步编程的作用(async / await)

    欢迎来到学习摆脱又加深内卷篇 下面是学习异步编程的应用 1.首先,我们建一个winfrom的项目,界面如下: 2.然后先写一个耗时函数: /// <summary> /// 耗时工作 // ...

  5. 【C# TAP 异步编程】三、async\await的运作机理详解

    [原创] 本文只是个人笔记,很多错误,欢迎指出. 环境:vs2022  .net6.0 C#10 参考:https://blog.csdn.net/brook_shi/article/details/ ...

  6. js异步编程终级解决方案 async/await

      在最新的ES7(ES2017)中提出的前端异步特性:async.await. async.await是什么 async顾名思义是“异步”的意思,async用于声明一个函数是异步的.而await从字 ...

  7. JavaScript异步编程:Generator与Async

    从Promise开始,JavaScript就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱. Promise是下边要讲的Generator/yield与async/await的基 ...

  8. C#~异步编程再续~你必须要知道的ThreadPool里的throw

    问题依旧存在 之前写过相关文章异步编程的文章,本文主要还是一点补充,之前在IIS经常发w3wp进程无做挂了的情况,但一直没能找到真正的原因,而查找相关资料,找了一些相关的文章,如await和async ...

  9. C#~异步编程再续~async异步方法与同步方法的并行

    返回目录 今天晚上没事写了个测试的代码,又看了看.net的并行编程,两个方法,一个是异步async修饰的,另一个是普通的方法,在控制台程序的Main方法里去调用这两个方法,会有什么结果呢? 首先我们看 ...

随机推荐

  1. Java中的多态

    1.多态:具有表现多种形态的能力的特征 父类: public abstract class Animal {public abstract void Say();} 子类: public class ...

  2. pdo的使用

    PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口. PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据. PDO随 ...

  3. 我这么玩Web Api(一):帮助页面或用户手册(Microsoft and Swashbuckle Help Page)

    前言 你需要为客户编写Api调用手册?你需要测试你的Api接口?你需要和前端进行接口对接?那么这篇文章应该可以帮到你.本文将介绍创建Web Api 帮助文档页面的两种方式,Microsoft Help ...

  4. AbpZero--2.如何启动

    1.直接启动 VS中直接启动 2.IIS站点 IIS中配置一个站点来启动(推荐) 3.登录 系统默认创建2个用户 默认用户名:admin 密码:123qwe 租户:Default  默认用户名:adm ...

  5. TCP/IP之TCP_NODELAY与TCP_CORK

    TCP/IP之Nagle算法与40ms延迟提到了Nagle 算法.这样虽然提高了网络吞吐量,但是实时性却降低了,在一些交互性很强的应用程序来说是不允许的,使用TCP_NODELAY选项可以禁止Nagl ...

  6. JavaScript中的this

    本文尽量避免概念性的讲解,那样太抽象 所以下面以一些简单的例子,从易到难对this的用法总结 1.方法中的this会指向当前执行该方法的对象 如: var name = "window&qu ...

  7. [斜率优化DP]【学习笔记】【更新中】

    参考资料: 1.元旦集训的课件已经很好了 http://files.cnblogs.com/files/candy99/dp.pdf 2.http://www.cnblogs.com/MashiroS ...

  8. 杂项之python描述符协议

    杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...

  9. 我的MYSQL学习心得(八) 插入 更新 删除

    我的MYSQL学习心得(八) 插入 更新 删除 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得( ...

  10. ASP.NET跨平台最佳实践

    前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语言本身来看,C#确实比Java ...