首先,我将解释我想要做的事情。
我有组件A 使用组件B 。
为了在两者之间进行通信,我需要使用事件。
我的一个先决条件是让组件B 以异步方式运行并以连续顺序运行事件处理程序。
此外,我想取消通话管道(当用户询问时)。因此,所有尚未执行的事件处理程序将永远不会执行。
实现的解决方案是TPL。我做了一个关于我试图做的事情的POC:
static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Factory.StartNew(() => DoSomeWork(token));
//.ContinueWith((prevTask) => DoSomeWork(token));
t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t);
Console.WriteLine("Finish");
Console.ReadKey();
}
static int id = 1;
static void DoSomeWork(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(1000);
Console.WriteLine(id++);
}
此代码段的输出:
1
完成
2
正如你所看到的,它在真正完成之前完成。它在完成后显示 2 。
如果我通过此修改前面的代码,它可以工作:
static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Factory.StartNew(() => DoSomeWork(token))
.ContinueWith((prevTask) => DoSomeWork(token));
//t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t);
Console.WriteLine("Finish");
Console.ReadKey();
}
static int id = 1;
static void DoSomeWork(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(1000);
Console.WriteLine(id++);
}
此代码段的输出:
1
2
完成
如您所知,我不需要在任务声明中使用continueWith语句,但是在引发事件时。
为什么Task.WaitAll(t);第一个样本不起作用吗?
有人可以帮助我吗?
答案 0 :(得分:1)
在C#中进行异步编码的正确方法是使用await
关键字。
public async Task DoLotsOfWork()
{
await DoSomeWorkAsync();
await DoSomeMoreWorkAsync();
Console.WriteLine("Finish");
}
从控制台应用程序运行该代码时会遇到一些问题,因此我建议您使用@ StephenCleary的Task.AsyncEx
库。
https://www.nuget.org/packages/Nito.AsyncEx/
你这样使用它。
public void Main()
{
AsyncContext.Run(DoLotsOfWork);
}
此外。使用Task.Run
(或更差Task.Factory.StartNew
)方法的原因很少。这些在后台运行你的方法作为Threadpool工作。
例如
private static async Task DoSomeWorkAsync(CancellationToken ct)
{
await Task.Delay(TimeSpan.FromMilliseconds(1000), ct);
Console.WriteLine(id++);
}
这不会在任何线程上运行(因此不会阻塞任何线程)。而是创建一个计时器/回调,以使主线程在1000毫秒后返回到第二行
编辑:要动态地执行此操作,这也非常简单
public async Task DoLotsOfWork(IEnumerable<Func<Task>> tasks)
{
foreach(var task in tasks)
await task();
Console.WriteLine("Finished");
}
如果你问一下使用糟糕的EAP模式的方法,我会建议你使用Rx的Observable.FromEventPattern
辅助函数。
public async Task SendEmail(MailMessage message)
{
using(var smtp = new SmtpClient())
{
smtp.SendAsync(message);
await Observable.FromEventPattern<>(x => smtp.SendCompleted +=x, x => smtp.SendCompleted -=x)
.ToTask()
}
}
进一步编辑:
public class Publisher
{
public IObservable<CancelationToken> SomeEvent {get;}
}
public abstract class Subscriber
{
public abstract IObservable<CancelationToken> Subscribe(IObservable<CancelationToken> observable);
}
IEnumerable<Subscriber> subscribers = ...
Publisher publisher = ...
IDisposable subscription = subscribers.Aggregate(publisher.SomeEvent, (e, sub) => sub.Subscribe(e)).Subscribe();
//Dispose the subscription when you want to stop listening.
答案 1 :(得分:1)
最初的问题是你正在创建两个任务,但只等待一个任务。
// t is now the continuation task
var t = Task.Factory.StartNew(() => DoSomeWork(token))
.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t); // <-- wait till the continuation task has finished
第二个片段会发生什么,你正在等待继续任务,所以它会等到它完成
// t is the "first" task
var t = Task.Factory.StartNew(() => DoSomeWork(token));
// The continuation task is assigned to "t2"
var t2 = t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(new [] { t, t2 } ); // <-- wait for all tasks
Console.WriteLine("Finish");
所以,第二种方法没问题,但如果你想要更精细的控制,只需指定一个任务变量来等待继续任务:
WaitAll
注意:我已按照您的代码进行了示例,但Task.Wait
并未将单个任务作为参数(它需要一系列任务),因此可能不编译。您可以使用WaitAll
或将数组传递给@Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
getSupportFragmentManager().beginTransaction().replace(R.id.viewpager, new Fragment_01_Rooms()).commit();
}