被传递给ParameterizedThreadStart的对象被覆盖?
背景:被传递给ParameterizedThreadStart的对象被覆盖?
我工作的一个小应用程序,将远程通过WMI读取的事件日志事件。基本上我正在寻找工作站何时锁定和解锁。
问题:
我创建的线程的阵列。我循环访问我的数据集(计算机名称),并使用自定义对象(LockHunterArgs)启动多个ParameterizedThreadStart对象。问题是我知道我的数据集没有重复。我在线程函数的末尾添加了一个console.writeline,它显示重复项。
另外,在我尝试使用线程之前。如果我同步运行代码,它的功能很好。它花了很长时间。所以这就是为什么我试图引入多线程。
示例输出:
//...Snipped一些独特的线的上方
计算机:COMP时间:2012年3月29日上午08点05分11秒会话:3935dd76-6a10-41a9 -bd96-86143c66482d 计算机:COMP时间:2012年3月29日上午08点05分11秒会议:3935dd76-6a10-41a9-bd96-86143c66482d
//...Snipped一些独特的和重复的线条下方
我的假设:
如果我在get_lock_data函数的前几行中放置一个断点,并在其中执行下一行。这是随机的。它会前进一次,然后再次击中同一条线。我甚至看到它下降两行,然后倒退。我认为这是因为我正在解决线程问题,并在不同的时间触及点,从而给出幻觉它正在倒退。但它几乎就像被传入的对象被后面的线程覆盖。
我试着创建另一个LockHunterArgs数组,并在线程启动过程中创建并分配它们,但那也不起作用。
这可能是一些愚蠢的东西。提前致谢。
//枪
代码:
public class LockHunterArgs
{
public LockHunterArgs(string comp, DateTime limit, Guid session)
{
Computer = comp;
LimitTime = limit;
sessionID = session;
}
public string Computer;
public DateTime LimitTime;
public Guid sessionID;
}
public class LockHunter
{
private void get_lock_data(object args)
{
string computer = ((LockHunterArgs)args).Computer;
DateTime limitTime = ((LockHunterArgs)args).LimitTime;
Guid sessionID = ((LockHunterArgs)args).sessionID;
//....SNippet ... code connects to the box and pulls data...
Console.WriteLine("Computer: " + computer + " Time: " + limitTime.ToString() + " Session: " + sessionID.ToString());
}
public void HuntLocks()
{
//....Snippet... code connects to database and gets a list of objects (currentSessions)
Thread[] threadArray = new Thread[currentSessions.Count];
int cnt = 0;
foreach (LINQ.session sesson in currentSessions)
{
DateTime mostRecentTimestamp = (from q in db.actions
where q.session_id == sesson.uid
orderby q.timestamp descending
select q.timestamp).FirstOrDefault();
ParameterizedThreadStart start = new ParameterizedThreadStart(get_lock_data);
threadArray[cnt] = new Thread(start);
threadArray[cnt].Start(new LockHunterArgs(sesson.computername , mostRecentTimestamp, sesson.uid));
cnt++;
}
for (int i = 0; i < threadArray.Length; i++)
{
threadArray[i].Join();
}
Console.WriteLine(DateTime.Now.ToString() + " Threads have joined");
//....Snippet of saving the gathered data from the threads to the database
}
}
解决方案:
我添加了一个新的类。然后遍历我的LINQ-to-SQL结果来创建新类的列表。然后我基于我的线程从该列表中启动,而不是LINQ-to-SQL生成的线程。一切都很好。任何人都可以解释吗?
public class TempSession
{
public TempSession(LINQ.session sess)
{
this.computername = sess.computername;
this.timestamp = sess.start_time;
this.uid = sess.uid;
}
public string computername;
public DateTime timestamp;
public Guid uid;
}
public void HuntLocks()
{
//select EventCode,TimeGenerated,Message from Win32_NTLogEvent WHERE logfile='Security' and (EventCode='4800' or EventCode='4801') and TimeGenerated > '20120327 08:08:08'
// 4800 = locked
// 4801 = unlocked
LINQ.Login_ActionsDataContext db = new LINQ.Login_ActionsDataContext();
List<LINQ.session> currentSessions = (from q in db.sessions
where q.end_time == null
orderby q.computername ascending
select q).ToList();
// START Solution Changes
List<TempSession> newCurrentSessions = new List<TempSession>();
foreach (LINQ.session session in currentSessions)
{
newCurrentSessions.Add(new TempSession(session));
}
Thread[] threadArray = new Thread[newCurrentSessions.Count];
// END solution changes
for (int i = 0; i < newCurrentSessions.Count; i++)
{
DateTime mostRecentTimestamp = (from q in db.actions
where q.session_id == newCurrentSessions[i].uid
orderby q.timestamp descending
select q.timestamp).FirstOrDefault();
ParameterizedThreadStart start = new ParameterizedThreadStart(get_lock_data);
threadArray[i] = new Thread(start);
threadArray[i].Start(new LockHunterArgs(newCurrentSessions[i].computername, mostRecentTimestamp, newCurrentSessions[i].uid));
}
for (int i = 0; i < threadArray.Length; i++)
{
threadArray[i].Join();
}
Console.WriteLine(DateTime.Now.ToString() + " Threads have joined");
db.actions.InsertAllOnSubmit(newActions);
Console.WriteLine(DateTime.Now.ToString() + " Found " + newActions.Count.ToString() + " locks");
db.SubmitChanges();
newActions = new List<LINQ.action>();
}
使用一个临时变量来存储迭代值:
foreach (LINQ.session sesson in currentSessions)
{
var tempSession = session; // now use tempSession
....
这是迭代值的闭合的已知副作用。
刚刚尝试过......相同的结果。 – 2012-03-29 15:17:41
@LanceIngle:那么'sesson'的_only_引用是什么时候分配给'tempSession'? (我看到3) – 2012-03-29 15:20:41
你确定你替换了所有引用的会话吗? – Aliostad 2012-03-29 15:20:59
我会说这个问题最有可能在你剔除的东西。我无法重现这个您的问题伪造数据:
var guids = Enumerable.Range(1, 10)
.Select(i => Guid.NewGuid())
.ToArray();
var currentSessions = Enumerable.Range(1, 10)
.Select(i => new {computername = "pc" + i})
.Zip(guids,(a,g) => new {a.computername, uid = g});
var dbactions = Enumerable.Range(1, 10)
.Select(i => DateTime.Now.AddHours(-1*i))
.Zip(guids, (t,g) => new {session_id = g, timestamp = t});
鉴于此,您可以提供一个工作的例子,是不依赖于任何本地资源?
我得到它的工作,我不明白它为什么它工作,虽然我添加了一个新的类,循环虽然我的LINQ到SQL结果,并创建了我的新类的列表。我的for循环和一切都很好。我能想到的唯一的事情是LINQ到SQL列表不是线程安全的吗? – 2012-03-29 17:37:51
你可能已经循环了关于你隐藏的东西的闭合变量,你必须显示该代码让我们知道肯定。 – 2012-03-29 17:59:23
我在解决方案中发布了上述所有内容(全功能)。 – 2012-03-29 18:09:56
因为我们知道这不是一个闭包问题,所以我的直觉让我认为它与在通过集合迭代时创建其他Linq To SQL调用有关。新的代码不会迭代一个linq到SQL生成列表,同时使另一个linq进入sql调用。它现在使用一个简单的for循环索引计数器。 – user957902 2012-03-29 21:45:58