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);
    }
}