I'm writing very simplistic WinForms app for college teachers.
Teacher will be able to send files/folders to student computers (30-35 workstations)
over the network to network share.
I already have working application and it works pretty well and suits all needs, but..
When I try to copy a folder that contains 25 subfolders, and 311 files total 7.22 MB
I start to see UI freezes and if I click and drag on a window it got to state "Not Responding"
after all work is done UI responsiveness returns.
I tried to use async over sync...
I create List<Task> tasks
for all 30 computers and await it with await Task.WhenAll(tasks)
以下是执行目录复制的代码:
private async Task CopyDirectoryAsync(string sourceDirectory, string destinationDirectory, string[] directories,
string[] files, string host, bool isCollectOperation)
{
try
{
if (isCollectOperation)
{
directories = Directory.GetDirectories(sourceDirectory, "*", _defaultEnumerationOptions);
files = Directory.GetFiles(sourceDirectory, "*.*", _defaultEnumerationOptions);
}
await CreateDirectoryAsync(destinationDirectory);
for (var i = 0; i < directories.Length; i++)
{
await CreateDirectoryAsync(@$"{destinationDirectory}{directories[i].Replace(sourceDirectory, "")}");
}
for (var i = 0; i < files.Length; i++)
{
await CopyFileAsync(files[i], $@"{files[i].Replace(sourceDirectory, destinationDirectory)}", null,
false);
}
SetHostLabelColor(host, Color.Green);
}
catch (Exception ex)
{
SetHostLabelColor(host, Color.Red);
await Log(ex);
}
}
复制文件:复制文件:
private async Task CopyFileAsync(string sourceFile, string destinationFile, string host, bool isFileCopy)
{
try
{
await using var sourceStream = new FileStream(
sourceFile,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
8192,
FileOptions.Asynchronous | FileOptions.SequentialScan);
await using var destinationStream = new FileStream(
destinationFile,
FileMode.OpenOrCreate, FileAccess.Write,
FileShare.None,
8192,
FileOptions.Asynchronous | FileOptions.SequentialScan);
await sourceStream.CopyToAsync(destinationStream);
if (isFileCopy)
{
SetHostLabelColor(host, Color.Green);
}
}
catch (Exception ex)
{
if (!isFileCopy) throw;
SetHostLabelColor(host, Color.Red);
await Log(ex);
}
}
我用Task.Run()包装了Directory.CreateDirectory()
private static async Task CreateDirectoryAsync(string path)
{
await Task.Run(() => { Directory.CreateDirectory(path); });
}
So my theory is that since Directory.CreateDirectory()
is strictly synchronous even I wrapped it in an asynchronous method with Task.Run()
- it still blocks message queue of WinForms app
I believe the same applies to Directory.GetDirectories()
and to Directory.GetFiles()
So my question is:
Is there a way in WinForms app to offload a synchronous operation in such a manner that it will not block UI thread, and preferably without using Control.BeginInvoke()
..
此CopyDirectory操作花费较长时间是可以的,因为它可能是SMB/NTFS/Win32或其他的限制.但为什么UI线程会被阻塞.
UPD:
When I hit Break All in a debugger
it almost always sits in this code
[LibraryImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "7.0.8.42427")]
private unsafe static SafeFileHandle CreateFilePrivate(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, SECURITY_ATTRIBUTES* lpSecurityAttributes, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile)
{
bool flag = false;
nint handle = 0;
SafeFileHandle safeFileHandle = new SafeFileHandle();
int lastSystemError;
try
{
try
{
fixed (char* ptr = &Utf16StringMarshaller.GetPinnableReference(lpFileName))
{
void* lpFileName2 = ptr;
Marshal.SetLastSystemError(0);
/* Always here --> */ handle = __PInvoke((ushort*)lpFileName2, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
lastSystemError = Marshal.GetLastSystemError();
}
}
finally
{
}
flag = true;
}
finally
{
if (flag)
{
Marshal.InitHandle(safeFileHandle, handle);
}
}
Marshal.SetLastPInvokeError(lastSystemError);
return safeFileHandle;
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", ExactSpelling = true)]
static extern unsafe IntPtr __PInvoke(ushort* lpFileName, int dwDesiredAccess, FileShare dwShareMode, SECURITY_ATTRIBUTES* lpSecurityAttributes, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
}