C# 如何使用Parallel.ForEach循环将文件上载到SFTP服务器以获得更好的性能
我尝试了C# 如何使用Parallel.ForEach循环将文件上载到SFTP服务器以获得更好的性能,c#,.net,task-parallel-library,sftp,parallel.foreach,C#,.net,Task Parallel Library,Sftp,Parallel.foreach,我尝试了Parallel.ForEach,但有些文件上载时大小为零。u每个回路正常工作,工作正常。但是速度很慢。有400万个文件。每个文件的大小为4MB public static bool UploadFiles( string ftpDirectory, string filePath, ConnectionInfo coninfo, string pattern) { using (var client = new SftpClient(coninfo)) {
Parallel.ForEach
,但有些文件上载时大小为零。u每个回路正常工作,工作正常。但是速度很慢。有400万个文件。每个文件的大小为4MB
public static bool UploadFiles(
string ftpDirectory, string filePath, ConnectionInfo coninfo, string pattern)
{
using (var client = new SftpClient(coninfo))
{
client.Connect();
client.ConnectionInfo.Timeout = TimeSpan.FromDays(2);
client.KeepAliveInterval = TimeSpan.FromSeconds(60);
// 4 millions files
var files = Directory.GetFiles(filePath, pattern , SearchOption.AllDirectories);
foreach (var file in files)
{
try
{
using (Stream inputStream = new FileStream(file, FileMode.Open))
{
client.UploadFile(inputStream, ftpDirectory + Path.GetFileName(file));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
};
client.Disconnect();
}
return true;
}
我已经将WinSCP库用于
Parallel.ForEach
循环。它工作得很好。
公共无效上载文件(字符串远程路径、字符串本地路径、字符串模式)
{
尝试
{
SessionOptions SessionOptions=新SessionOptions
{
协议=协议.Sftp,
主机名=sftpConfig[“主机”],
用户名=sftpConfig[“用户名”],
密码=”,
SshHostKeyFingerprint=“ssh rsa 2048 xxx”,
SshPrivateKeyPath=_sftpConfig[“sftprivatekeypath”]
};
常数int=3;
DateTime start=DateTime.Now;
整数计数=0;
Int64字节=0;
WriteLine(“启动文件枚举…”);
IEnumerable files=Directory.GetFiles(localPath、pattern、SearchOption.AllDirectories);
IEnumerator fileEnumerator=files.GetEnumerator();
列表任务=新列表();
HashSet existingremotepath=新HashSet();
对于(int i=1;i
{
使用(会话上载会话=新会话())
{
while(true)
{
字符串localFilePath;
锁(文件枚举器)
{
如果(!FileEnumerator.MoveNext())
{
打破
}
localFilePath=filesEnumerator.Current;
字节+=新文件信息(localFilePath).Length;
计数++;
}
如果(!uploadSession.Opened)
{
WriteLine(“正在开始上载{0}…”,否);
uploadSession.Open(会话选项);
}
字符串remoteFilePath=Path.Combine(remotePath,Path.GetFileName(localFilePath));
WriteLine(“将{0}上载到{2}中的{1}…”,localFilePath,remoteFilePath,no);
uploadSession.put文件(
localFilePath、RemotePath.EscapeFileTask(remoteFilePath))。
检查();
}
如果(uploadSession.Opened)
{
WriteLine(“上传{0}完成”,否);
}
其他的
{
WriteLine(“上传{0}无事可做”,否);
}
}
});
任务。添加(任务);
task.Start();
}
WriteLine(“等待上传完成…”);
Task.WaitAll(tasks.ToArray());
控制台。写入线(“完成”);
DateTime end=DateTime.Now;
WriteLine(“take{0}”,(end-start));
WriteLine(“上载的{0}个文件,总计{1:N0}个字节”,计数,个字节);
}
捕获(例外e)
{
WriteLine(“错误:{0}”,e);
}
}
存在异步重载:SftpClient.BeginUploadFile
。尽管您仍然需要在每个线程中使用新连接。所以它不像使用异步API那么容易。这可能也是Parallel.ForEach
失败的原因。与上面的评论相反,我相信使用Parallel.ForEach
并没有错,只要您保证不会并行使用相同的连接。看见
public void UploadFiles(string remotePath, string localPath, string pattern)
{
try
{
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = _sftpConfig["Host"],
UserName = _sftpConfig["UserName"],
Password = "",
SshHostKeyFingerprint = "ssh-rsa 2048 xxx",
SshPrivateKeyPath = _sftpConfig["SftpPrivateKeyPath"]
};
const int batches = 3;
DateTime started = DateTime.Now;
int count = 0;
Int64 bytes = 0;
Console.WriteLine("Starting files enumeration...");
IEnumerable<string> files = Directory.GetFiles(localPath, pattern, SearchOption.AllDirectories);
IEnumerator<string> filesEnumerator = files.GetEnumerator();
List<Task> tasks = new List<Task>();
HashSet<string> existingRemotePaths = new HashSet<string>();
for (int i = 1; i <= batches; i++)
{
int no = i;
Task task = new Task(() =>
{
using (Session uploadSession = new Session())
{
while (true)
{
string localFilePath;
lock (filesEnumerator)
{
if (!filesEnumerator.MoveNext())
{
break;
}
localFilePath = filesEnumerator.Current;
bytes += new FileInfo(localFilePath).Length;
count++;
}
if (!uploadSession.Opened)
{
Console.WriteLine("Starting upload {0}...", no);
uploadSession.Open(sessionOptions);
}
string remoteFilePath = Path.Combine(remotePath,Path.GetFileName(localFilePath)) ;
Console.WriteLine("Uploading {0} to {1} in {2}...",localFilePath, remoteFilePath, no);
uploadSession.PutFiles(
localFilePath, RemotePath.EscapeFileMask(remoteFilePath)).
Check();
}
if (uploadSession.Opened)
{
Console.WriteLine("Upload {0} done", no);
}
else
{
Console.WriteLine("Upload {0} had nothing to do", no);
}
}
});
tasks.Add(task);
task.Start();
}
Console.WriteLine("Waiting for uploads to complete...");
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Done");
DateTime ended = DateTime.Now;
Console.WriteLine("Took {0}", (ended - started));
Console.WriteLine("Uploaded {0} files, totaling {1:N0} bytes", count, bytes);
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e);
}
}