我的问题是将 this.folderFolder
实例方法传递给 ThreadStart
构造函数。我使用 dirAssThread
单步执行它并观察它正确更新实例数据成员并完成,然后我回到
if (dirAssThread.IsAlive) completeThread(-1); //***ie abort
并发现我通过方法传递给 ThreadStart
构造函数的同一个 this
实例的数据成员已奇迹般地将自身重置为 0!
这里是其他函数
using System;
using System.IO;
using System.Threading;
namespace MonitorService
{
struct myStruct
{
long bytesSzMember;
Thread dirAssThread;
private Object thisLock;
private void completeThread(long bytesSzNew)
{
lock (thisLock)
{
if (bytesSzNew == -1)
{
dirAssThread.Abort();
Console.WriteLine("A thread timed out.");
}
else
{
bytesSzMember = bytesSzNew;
Console.WriteLine("A thread update size.");
}
}
}
private void folderFolder()
{
long bytesSzNew = 0;
DirectoryInfo di = new DirectoryInfo("C:\\SomeDir");
DirectoryInfo[] directories = di.GetDirectories("*",SearchOption.AllDirectories);
FileInfo[] files = di.GetFiles("*",SearchOption.AllDirectories);
foreach (FileInfo file in files)
{
bytesSzNew += file.Length;
}
completeThread(bytesSzNew);
}
private void updateSize()
{
thisLock = new Object();
dirAssThread = new Thread(new ThreadStart(this.folderFolder));
dirAssThread.Start();
Thread.Sleep(5000);
if (dirAssThread.IsAlive) completeThread(-1);
}
}
}
最佳答案
更新
问题标题更新后,您看到的问题是结构是根据引用复制的。将委托(delegate)分配给线程时,您正在传递结构的副本,线程将更新该副本。当您检查 completeThread
时,它是针对尚未更新的原始文件。
使用类而不是结构。
替代解决方案
我建议使用等待句柄而不是 sleep 和线程中止,因为 Thread.Abort
被认为是一种危险的做法,应该避免(在这种情况下很容易)。我提出以下解决方案,它是一个递归版本,不会遵循循环引用(因此实际上不需要中止,如果您不想要超时设施,可以删除代码)。
public class WaitForFileSizes
{
private readonly object _syncObj = new object();
private readonly HashSet<string> _seenDirectories = new HashSet<string>();
private readonly ManualResetEvent _pEvent = new ManualResetEvent(false);
private long _totalFileSize;
private Thread _thread;
private volatile bool _abort;
private void UpdateSize()
{
_thread = new Thread(GetDirectoryFileSize);
_thread.Start();
bool timedout = !_pEvent.WaitOne(5000);
if (timedout)
{
_abort = true;
_pEvent.WaitOne();
Console.WriteLine("A thread timed out.");
}
else
{
Console.WriteLine("Total size {0}b.", _totalFileSize);
}
}
private void GetDirectoryFileSize()
{
GetDirectoryFileSizesRecursively(new DirectoryInfo("C:\\temp"));
_pEvent.Set();
}
private void GetDirectoryFileSizesRecursively(DirectoryInfo dir)
{
Parallel.ForEach(dir.EnumerateFiles(), f =>
{
if (_abort)
{
_pEvent.Set();
return;
}
Interlocked.Add(ref _totalFileSize, f.Length);
});
Parallel.ForEach(dir.EnumerateDirectories(), d =>
{
if (!IsSeen(d))
{
GetDirectoryFileSizesRecursively(d);
}
});
}
private bool IsSeen(DirectoryInfo dir)
{
lock (_syncObj)
{
if (!_seenDirectories.Contains(dir.FullName))
{
_seenDirectories.Add(dir.FullName);
return false;
}
return true;
}
}
}
更新
因为我们现在有循环引用检测,所以可以删除线程和中止代码,因为如果线程处于无限循环中,以前在那里会中止线程——现在不需要了:
public class WaitForFileSizes
{
private readonly object _syncObj = new object();
private readonly HashSet<string> _seenDirectories = new HashSet<string>();
private long _t;
public void UpdateSize()
{
GetSize(new DirectoryInfo("C:\\temp"));
Console.WriteLine("Total size {0}b.", _t);
}
private void GetSize(DirectoryInfo dir)
{
Parallel
.ForEach(dir.EnumerateFiles(), f => Interlocked.Add(ref _t, f.Length));
Parallel
.ForEach(dir.EnumerateDirectories().Where(IsNewDir), GetSize);
}
private bool IsNewDir(FileSystemInfo dir)
{
lock (_syncObj)
{
if (!_seenDirectories.Contains(dir.FullName))
{
_seenDirectories.Add(dir.FullName);
return true;
}
return false;
}
}
}
关于c# - 将(结构的)实例方法传递给 ThreadStart 似乎更新了一个虚假实例,因为原始实例不受影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7318225/