Queue存储要执行的异步方法,如何让异步方法变成同步方法

张天星 2019-02-05 09:05:31
需要不定时执行不同的,消耗时间的任务。
我用了public Queue<Action>来存储这些需要运行的方法队列。
可问题来了,我这些委托到队列里面的方法是异步方法。
LoopProcs()中,handler.DynamicInvoke();根本不会停顿,
第一个方法还没有执行出结果来,就开始运行第二个,第三个方法。。。
于是我开始尝试将异步改成同步的,我将public void IsLogin()中的!await formCefSharp.LoginNameAsync()改成了!formCefSharp.LoginNameAsync().GetAwaiter().GetResult(),不能用,不能用的原因我写在最后面。
于是我将 LoopProcs()中,有方法开始运行,调用了handler.DynamicInvoke();之后,就return结束 LoopProcs()。
等handler.DynamicInvoke();中的方法执行结束之后,再调用一次LoopProcs()。
勉强达到了我的要求,代码如下,询问有没有办法改进,我感觉自己这么写,目的似乎达到了(不知道有没有后遗症),但过程太笨重,不知道有没有好的设计方式。
我这个项目的地址是:https://gitee.com/zzwtx/FictionsDownload.git
我所说的内容在FictionsDownload.BLL.CefSharpQueue
public Queue<Action> Procs = new Queue<Action>();
/// <summary>
/// 添加一个任务
/// </summary>
/// <param name="proc"></param>
public void Register(Action proc)
{
Procs.Enqueue(proc);
}
///循环执行任务
public async void LoopProcs()
{
while (true)
{
if (Procs.Count > 0)
{
var handler = Procs.Dequeue();
///这里一个Action执行之后,立刻就执行了下一个Action,我感觉是因为Action是async的缘故,可以应该怎么修改,让它执行完了再执行下一个方法?
handler.DynamicInvoke();
///无可奈何,这里有任务运行就中断循环,然后在执行的方法中,方法结束的时候再调用一次LoopProcs();
return;
}
else
{
await Task.Delay(1000);
}
}
}
/// <summary>
/// 等待浏览器初始化 只能用异步,用同步的话,会卡死UI,我又不想用多线程。
/// </summary>
public void IsBrowserInit()
{
this.Register(() =>
{
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "开始运行IsBrowserInit()"));
for (int i = 0; !formCefSharp.IsBrowserInitialized; i++)
{
if (i > 20)
{
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "浏览器未初始化,已经等待20秒,中断"));
LoopProcs();
return;
}
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "浏览器未初始化,等待1秒,次数:" + i));
Task.Delay(1000);
}
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "浏览器初始化完成"));
LoopProcs();
return;
});

/// <summary>
/// 判断是否登陆
/// </summary>
public void IsLogin()
{
this.Register(async () => {
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "开始检查登陆状态"));
if (!await formCefSharp.LoginNameAsync())
{
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "帐号未登录,中断"));
LoopProcs();
return;
}
else
{
this.AddMessage.Invoke(this, new AddMessageEventArgs(FormQidian.ErtboxMsg.添加, "帐号已登录"));
}
LoopProcs();
return;
});
}

这里是我做过的一些尝试:
1、我将IsLogin改成了同步方法, 就是
public void IsLogin()
{
this.Register(async () => {
if (!await formCefSharp.LoginNameAsync().GetAwaiter().GetResult())
{
return;
}
return;
});
}
public async Task<bool> LoginNameAsync()
{
for (int i = 1; i < 5; i++)
{
if (UserName == null)
{
webBrowser.GetBrowser().Reload();
//这里卡死,5秒后也不会继续
await Task.Delay(5000);
}
else
{
return true;
}
}
return false;
}

如代码所示,await Task.Delay(5000);那里卡死了,我不知道什么原因。
但不管什么原因,这里不用异步,直接改成同步总可以吧。

public void IsLogin()
{
this.Register(() => {
if (!formCefSharp.LoginName())
{
return;
}
return;
});
}
public bool LoginName()
{
for (int i = 1; i < 5; i++)
{
if (UserName == null)
{
webBrowser.GetBrowser().Reload();
System.Threading.Thread.Sleep(5000);
}
else
{
return true;
}
}
return false;
}


这样改成同步之后,卡上几秒倒是可以了,可是,UI卡几秒,这不能用啊,我感觉奇怪,

public async void LoopProcs()
{
while (true)
{
if (Procs.Count > 0)
{
var handler = Procs.Dequeue();
///这里一个Action执行之后,立刻就执行了下一个Action,我感觉是因为Action是async的缘故,可以应该怎么修改,让它执行完了再执行下一个方法?
handler.DynamicInvoke();
return;
}
else
{
await Task.Delay(1000);
}
}
}

这个方法里面,要怎么写,才能调用同步方法,又不会卡死UI呢。
我想不出来。
绕了一圈之后,还是回到最上面的代码中,方法是异步的,loop有方法调用后就return,异步的方法执行结束之后就启动loop。
求指点...
...全文
339 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
xuzuning 2019-02-08
  • 打赏
  • 举报
回复
while + Sleep 并不是什么大逆不道的玩意 Sleep 并没有阻塞任何东西,只不过是使当前线程暂时停顿一下,让其他线程得以先运行 Task 就是异步运行,要改变这个默认行为,需调用它的 Wait() 方法(等待运行结束) async / await 结构是用同步形式书写的异步程序,不想异步就不要 async / await Action 是无参数、无返回的委托,协调一组依次执行的 Action 需在委托体内部进行(比如全局的信号量)
张天星 2019-02-08
  • 打赏
  • 举报
回复
要结帖了,多谢楼上回答,
本次的问题,其实主要就是异步转同步的问题,
百度了一些博客之后,大概知道了一个结论,
从UI上用异步去处理事情,那么后续的所有调用,最好都用异步一直传递下去。
否则在转同步的时候,就会同步到UI线程上,造成UI卡死。
处理的办法有,但不建议操作,如果真有需要同步操作,直接就是另起一个线程,才不会堵塞UI。
我是决定一直异步调用下去了。
多谢楼上回答。
张天星 2019-02-06
  • 打赏
  • 举报
回复
引用 3 楼 以专业开发人员为伍 的回复:
实际上只要看“5000毫秒”就知道有多么荒唐幼稚,你怎么知道不应该是4900毫秒?怎么知道不应该是5100毫秒?怎么知道不应该是14.9毫秒?你这个5000毫秒纯粹是胡乱瞎猜的,需要不断地手动胡乱“调整”。这种告诉用户说要手动瞎猜的编程方式是一个正规程序员该干的事情吗?

额。。哪能说说应该怎么搞么。这个我搞错了可以再改。
  • 打赏
  • 举报
回复
实际上只要看“5000毫秒”就知道有多么荒唐幼稚,你怎么知道不应该是4900毫秒?怎么知道不应该是5100毫秒?怎么知道不应该是14.9毫秒?你这个5000毫秒纯粹是胡乱瞎猜的,需要不断地手动胡乱“调整”。这种告诉用户说要手动瞎猜的编程方式是一个正规程序员该干的事情吗?
  • 打赏
  • 举报
回复
编程开发的大忌,就是胡乱写什么“while 循环+Sleep阻塞”代码。一看这种代码,就知道脑子里没有异步概念。
  • 打赏
  • 举报
回复
既然你不理解异步方法,为什么赶时髦用什么 await? 根本不懂什么叫作异步,却用异步名词儿来乱写代码,自然是一眼就能看出你满脑子只有同步阻塞概念,根本不理解异步。就算是有100个异步方法,也是可以异步执行完一个方法再来执行下一个异步方法,代码也是简洁直接地使用 await 或者回调方式。而你根本不理解异步概念,也就无法理解按顺序依次执行异步方法的最浅显的概念。
threenewbee 2019-02-05
  • 打赏
  • 举报
回复
异步方法变成同步方法最简单了,用 Task<返回值类型>调用你的异步方法,然后 Task.Wait()

110,499

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧