我想使用.NET-FTP Libary (http://netftp.codeplex.com)。libary提供了BeginOpenRead(string,AsyncCallback,object)来使用异步编程模型下载内容。回调的实现与示例基本相同:
static void BeginOpenReadCallback(IAsyncResult ar) {
FtpClient conn = ar.AsyncState as FtpClient;
try {
if (conn == null)
throw new InvalidOperationException("The FtpControlConnection object is null!");
using (Stream istream = conn.EndOpenRead(ar)) {
byte[] buf = new byte[8192];
try {
DateTime start = DateTime.Now;
while (istream.Read(buf, 0, buf.Length) > 0) {
double perc = 0;
if (istream.Length > 0)
perc = (double)istream.Position / (double)istream.Length;
Console.Write("\rTransferring: {0}/{1} {2}/s {3:p} ",
istream.Position.FormatBytes(),
istream.Length.FormatBytes(),
(istream.Position / DateTime.Now.Subtract(start).TotalSeconds).FormatBytes(),
perc);
}
}
finally {
Console.WriteLine();
istream.Close();
}
}
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
finally {
m_reset.Set();
}
}在异步方法的工作完成之后,如果触发一个已完成的事件(由启动异步方法的线程以避免UI问题)将结果传递给主线程,那就太好了。就像BackgroundWorker一样(使用RunWorkerCompleted)。
我怎么能意识到这一点?
发布于 2014-01-29 21:57:45
尝试将APM模式转换为水龙头模式(更多信息):
static public Task<Stream> OpenReadAsync(FtpClient ftpClient, string url)
{
return Task.Factory.FromAsync(
(asyncCallback, state) =>
ftpClient.BeginOpenRead(url, asyncCallback, state),
(asyncResult) =>
ftpClient.EndOpenRead((asyncResult));
}然后可以使用async/await,不必担心同步上下文:
Stream istream = await OpenReadAsync(ftpClient, url); 此外,您还可以使用Stream.ReadAsync
while (await istream.ReadAsync(buf, 0, buf.Length) > 0)
{
// ...
}BackgroundWorker被基于任务的API所取代,所以它可能是双赢的情况(更多信息:Task.Run对BackgroundWorker和这里)。
UPDATE --如果您在VS2012+中工作,可以使用Microsoft.Bcl.Async来针对.NET 4.0,并且仍然使用现代语言和TPL特性,比如async/await。我已经经历过了,我强烈推荐它,因为它使得将来移植到.NET 4.5非常容易。
否则,您可以使用Task.ContinueWith(callback, TaskScheduler.FromCurrentSynchronizationContext())在UI线程上继续。这是一个相关实例。
发布于 2014-01-29 21:29:12
最简单的方法是将SynchronizationContext传递给BeginOpenRead并在回调中使用它。
private class StateHolder
{
public StateHolder(FtpClient client, SynchronizationContext context)
{
Client = client;
Context = context;
//SynchronizationContext.Current can return null, this creates a new context that posts to the Thread Pool if called.
if(Context == null)
Context = new SynchronizationContext();
}
public FtpClient Client {get; private set;}
public SynchronizationContext Context {get; private set;}
}
//...
ftpClient.BeginOpenRead(someString,BeginOpenReadCallback, new StateHolder(ftpClient, SynchronizationContext.Current));然后在回调中使用传入的状态对象。
void BeginOpenReadCallback(IAsyncResult ar)
{
StateHolder state = ar.AsyncState as StateHolder;
FtpClient conn = state.client;
//... Everything else the same in the function.
//state.Context can't be null because we set it in the constructor.
state.Context.Post(OnCompleted, conn);
}
protected virtual void OnCompleted(object state) //I use object instead of FtpClient to make the "state.Context.Post(OnCompleted, conn);" call simpler.
{
var conn = state as FtpClient;
var tmp = Completed; //This is the event people subscribed to.
(tmp != null)
{
tmp(this, new CompletedArgs(conn)); //Assumes you followed the standard Event pattern and created the necessary classes.
}
}https://stackoverflow.com/questions/21442523
复制相似问题