我正在使用Starksoft.Net.Ftp为上传执行异步操作。
看起来像这样:
public void UploadFile(string filePath, string packageVersion)
{
_uploadFtpClient= new FtpClient(Host, Port, FtpSecurityProtocol.None)
{
DataTransferMode = UsePassiveMode ? TransferMode.Passive : TransferMode.Active,
FileTransferType = TransferType.Binary,
};
_uploadFtpClient.TransferProgress += TransferProgressChangedEventHandler;
_uploadFtpClient.PutFileAsyncCompleted += UploadFinished;
_uploadFtpClient.Open(Username, Password);
_uploadFtpClient.ChangeDirectoryMultiPath(Directory);
_uploadFtpClient.MakeDirectory(newDirectory);
_uploadFtpClient.ChangeDirectory(newDirectory);
_uploadFtpClient.PutFileAsync(filePath, FileAction.Create);
_uploadResetEvent.WaitOne();
_uploadFtpClient.Close();
}
private void UploadFinished(object sender, PutFileAsyncCompletedEventArgs e)
{
if (e.Error != null)
{
if (e.Error.InnerException != null)
UploadException = e.Error.InnerException;
}
_uploadResetEvent.Set();
}
如您所见,其中有一个ManualResetEvent,它被声明为类顶部的私有变量:
private ManualResetEvent _uploadResetEvent = new ManualResetEvent(false);
嗯,感觉只是它应该等待上传完成,但它必须是异步的才能报告进度,仅此而已。
现在,这很好用。如果愿意,我还有第二种方法可以取消上传。
public void Cancel()
{
_uploadFtpClient.CancelAsync();
}
当上传被取消时,服务器上的一个目录也必须被删除。我也有一个方法:
public void DeleteDirectory(string directoryName)
{
_uploadResetEvent.Set(); // As the finished event of the upload is not called when cancelling, I need to set the ResetEvent manually here.
if (!_hasAlreadyFixedStrings)
FixProperties();
var directoryEmptyingClient = new FtpClient(Host, Port, FtpSecurityProtocol.None)
{
DataTransferMode = UsePassiveMode ? TransferMode.Passive : TransferMode.Active,
FileTransferType = TransferType.Binary
};
directoryEmptyingClient.Open(Username, Password);
directoryEmptyingClient.ChangeDirectoryMultiPath(String.Format("/{0}/{1}", Directory, directoryName));
directoryEmptyingClient.GetDirListAsyncCompleted += DirectoryListingFinished;
directoryEmptyingClient.GetDirListAsync();
_directoryFilesListingResetEvent.WaitOne(); // Deadlock appears here
if (_directoryCollection != null)
{
foreach (FtpItem directoryItem in _directoryCollection)
{
directoryEmptyingClient.DeleteFile(directoryItem.Name);
}
}
directoryEmptyingClient.Close();
var directoryDeletingClient = new FtpClient(Host, Port, FtpSecurityProtocol.None)
{
DataTransferMode = UsePassiveMode ? TransferMode.Passive : TransferMode.Active,
FileTransferType = TransferType.Binary
};
directoryDeletingClient.Open(Username, Password);
directoryDeletingClient.ChangeDirectoryMultiPath(Directory);
directoryDeletingClient.DeleteDirectory(directoryName);
directoryDeletingClient.Close();
}
private void DirectoryListingFinished(object sender, GetDirListAsyncCompletedEventArgs e)
{
_directoryCollection = e.DirectoryListingResult;
_directoryFilesListingResetEvent.Set();
}
由于取消时没有调用上传的完成事件,我需要在DeleteDirectory方法中手动设置ResetEvent。
现在,我在这里做什么:我首先列出目录中的所有文件以便删除它们,因为无法删除已填充的文件夹。
此方法GetDirListAsync也是异步的,这意味着我需要另一个ManualResetEvent,因为我不希望表单冻结。
此 ResetEvent 是_directoryFilesListingResetEvent。它像上面的_uploadResetEvent一样声明。
现在,问题是,它转到_directoryFilesListingResetEvent的 WaitOne 调用,然后卡住了。一个死锁并且表单冻结。(我也在代码中标记了它)
这是为什么?我试图移动_uploadResetEvent.Set()的调用的调用,但它没有改变。有没有人看到问题?
当我尝试调用DeleteDirectory -方法而不进行任何上传时,它也可以正常工作。我认为问题在于两个 ResetEvents 都使用相同的资源或其他东西并且自己重叠,我不知道。
谢谢你的帮助。