我得到了下面的多线程代码摘录,我一直在研究它来比较压缩拷贝和解压缩后的文件。应用程序正在压缩一个包含不同大小的可变数量文件的文件夹,将这些文件复制到服务器,然后解压缩它们。然后对这些文件进行比较,并将这种比较执行到ThreadPool
。
下面是当前的完整方法:
public void FolderMoverLogic(string folderPathToZip, string unzipOutputDir)
{
string folderRootDir = Path.GetDirectoryName(folderPathToZip);
string folderNameToZip = Path.GetFileName(folderPathToZip);
try
{
//Zips files in <folderPathToZip> into folder <zippedLocal>
TransferMethods.CreateZipExternal(folderPathToZip, zippedlocal);
//Copies zipped folder to server location
File.Copy(zippedlocal + "\" + folderNameToZip + ".zip", zippedserver + "\" + folderNameToZip + ".zip");
//Unzips files to final server directory
TransferMethods.UnZip(zippedserver + "\" + folderNameToZip + ".zip", unzipOutputDir + "\" + folderNameToZip, sizeof(Int32));
TransferMethods m = new TransferMethods();
//Enumerate Files for MD5 Hash Comparison
var files = from file in Directory.EnumerateFiles(folderPathToZip, "*", SearchOption.AllDirectories)
select new
{
File = file,
};
int fileCount = 0;
CountdownEvent countdown = new CountdownEvent(10000);
using (ManualResetEvent resetEvent = new ManualResetEvent(false))
{
foreach (var f in files)
{
Interlocked.Increment(ref fileCount);
countdown.Reset(fileCount);
try
{
ThreadPool.QueueUserWorkItem(
new WaitCallback(c =>
{
//Check if any of the hashes have been different and stop all threads for a reattempt
if (m.isFolderDifferent)
{
resetEvent.Set();
CancellationTokenSource cts = new CancellationTokenSource();
cts.Cancel(); // cancels the CancellationTokenSource
try
{
countdown.Wait(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("cde.Wait(preCanceledToken) threw OCE, as expected");
}
return;
}
else
{
//Sets m.isFolderDifferent to true if any files fail MD5 comparison
m.CompareFiles(f.File, folderRootDir, unzipOutputDir);
}
if (Interlocked.Decrement(ref fileCount) == 0)
{
resetEvent.Set();
}
countdown.Signal();
}));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
countdown.Wait();
resetEvent.WaitOne();
resetEvent.Close();
}
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
}
}
目前看过的有用资源:
发出信号并立即关闭ManualResetEvent是否安全?
停止。net线程池中的所有线程?
MSDN CountdownEvent
ThreadPool逻辑要求:
- 比较本地和服务器上的所有枚举文件
- 如果哈希不匹配,从所有线程返回
前一个线程池代码:
using (ManualResetEvent resetEvent = new ManualResetEvent(false))
{
foreach (var f in files)
{
testCount++;
try
{
//Thread t = new Thread(() => m.CompareFiles(f.File, unzipped, orglsource));
//t.Start();
//localThreads.Add(t);
ThreadPool.QueueUserWorkItem(
new WaitCallback(c =>
{
if (resetEvent.WaitOne(0)) //Here is the `ObjectDisposedException`
{
return;
}
if (!m.Folderdifferent)
{
m.CompareFiles(f.File, folderRootDir, unzipOutput);
}
else
{
resetEvent.Set();
}
if (Interlocked.Decrement(ref fileCountZipped) == 0)
{
resetEvent.Set();
}
}));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
resetEvent.WaitOne();
}
我得到ObjectDisposedExceptions
周期性与前面的代码所示。
我的问题是:
- 当前方法是线程安全的吗?
- 逻辑是否正确?
- 关于性能或线程安全的改进意见
- 我在顶部的当前方法是否解决了以前的代码异常
我一直在测试这段代码,它一直在工作,没有例外,但我正在寻找一些更有经验的反馈。
一些注意事项:
- 不应该是这样的吗?:
CountdownEvent countdown = new CountdownEvent(files.Count());
- 安全吗?- NO -我只是不喜欢countdowevent的想法,如果任何操作与任何文件失败,你没有得到信号和应用程序挂起倒计时。wait(),我更喜欢使用TPL任务代替-而不是
countdown.Wait()
和使用Task.WaitAll(tasks)
-
永远不要在线程中直接使用"foreach变量"(这个线程解释了为什么),所以代替:
foreach (var f in files) { Task.Run(() => { var whateveryDoWithIt = f.File; } }
foreach (var f in files) { var ftemp = f; Task.Run(() => { var whateveryDoWithIt = ftemp.File; } }
-
回答如果它是线程安全的我会回答:是的,如果你修复上面的点和所有的方法使用它也是线程安全的