我正在通过TCP / IP实现客户端协议MyProtocol。协议的Connect()方法应该具有类似于TcpClient.ConnectAsync()的签名 - 也就是说,它应该返回一个任务:
Task MyProtocol.Connect (…);
此方法(MyProtocol.Connect()
)应进行异步TCP / IP连接(通过TcpClient.ConnectAsync()
),返回未完成的任务T,然后定期向服务器发送特定消息M - 再次异步(通过NetworkStream.WriteAsync()
)。当从服务器收到某个响应R时 - 再次异步(通过NetworkStream.ReadAsync()
),MyProtocol.Connect()
应该完成任务T.
我正在做以下事情:
// Client of the protocol:
var task = myProtocol.Connect(); // asynchronous call, we don’t want to wait until connected
task.ContinueWith(t =>
{
// Connected – doing an OnConnected stuff
…
});
// MyProtocol.Connect() implementation:
public class MyProtocol
{
private Task connectTask;
public Task Connect(…)
{
var tcpIpConnectTask = mTcpIpProtocol.Connect(…);
tcpIpConnectTask.ContinueWith(t =>
{
connectTask = new Task();
}
return connectTask;
}
}
显然必须通过计时器定期向服务器发送消息M.一旦从服务器异步接收到响应R,connectTask
必须标记为已完成,我看不到这样做的方法。
好吧,严格来说,我已设法将connectTask
标记为已完成;我将它包装在TaskCompletionSource<bool>
中并使用TaskCompletionSource.SetResult(true)
。
但是我想知道这是否是唯一的,更不用说完成我需要的最好方法了?我特别不喜欢TaskCompletionSource<>
必须具有非void任务结果类型(我使用bool)的事实,即没有非泛型版本。
在TPL到达之前,我们有自己的类似框架和方法Task.NotifyCompleted()
,因此我们可以在一个地方创建一个任务,并在另一个地方将其标记为已完成 - 所有这些都是异步工作的。但是我在TPL中读到的关于任务的所有内容似乎都暗示,只有当委托的代理运行到最后一行时,任务才会完成...
或者我错过了一些简单的东西?
答案 0 :(得分:3)
由于您已经在使用.Net 4.5,因此您应该使用C#5.0 async
- await
,这意味着完全适合这种情况。代码可能看起来像(有些伪编码):
public Task ConnectAsync()
{
await ClientConnectAsync();
while (true)
{
await SendMessageAsync();
var response = await ReceiveMessageAsync();
if (response == R)
return;
await Task.Delay(period);
}
}
要真正回答您的问题:
但是我想知道这是否是唯一的,更不用说完成我需要的最佳方式了?
如果您不想使用async
- await
,那么是,TaskCompletionSource
是执行此操作的唯一通用方法。
我特别不喜欢
TaskCompletionSource<>
必须具有非void任务结果类型(我使用bool)的事实,即没有非泛型版本。
这有点令人讨厌,但这并不重要,因为Task<T>
继承自Task
。
但我在TPL中读到的任何内容似乎都暗示,只有当委托的代表运行到最后一行时,任务才会完成[...]
对于有代理要运行的Task
,情况属实(可以使用Task.Run()
,Task.Factory.StartNew()
或new Task()
创建代理。但它不适用于没有委托的Task
,可以使用async
- await
或TaskCompletionSource
创建。
答案 1 :(得分:1)
我没有对此进行过测试,但有可能通过多个任务延续来实现您想要的效果,并且一旦收到所需的响应,请将connectedTask
设置为从上一个ReadAsync
收到的任务
public class MyProtocol
{
private Task connectTask;
public Task Connect()
{
var tcpIpConnectTask = mTcpIpProtocol.Connect();
tcpIpConnectTask.ContinueWith(t =>
{
bool finished = false;
Task readAsyncTask = null;
while(!finsihed)
{
NetworkStream.WriteASync().
ContinueWith(t1 =>
{
if(t1.Exception == null)
{
readAsyncTask = NetworkStream.ReadASync().
ContinueWiht(t2 =>
{
if(t2.Exception == null)
{
if(t2.Result == /*Desired Response*/)
{
finished = true;
connectedTask = t2;
}
}
else
{
// Handle ReadAsync error
}
}, null, TaskScheduler.Default);
}
else
{
// handle WriteAsync error
}
}, null, TaskScheduler.Default);
// Wait for the read operation to complete. This can cause a deadlock
// when called from a UI thread as Wait() is a blocking call. Ensure we are waiting
// on a background thread by specifying TaskScheduler.Default
readAsyncTask.Wait();
}
}, null, TaskScheduler.Default);
return connectTask;
}
或者,如果您希望任务具有特定结果,则可以返回Task.FromResult(..)
,这也是一项已完成的任务。