被传递给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>(); 

    } 
+0

因为我们知道这不是一个闭包问题,所以我的直觉让我认为它与在通过集合迭代时创建其他Linq To SQL调用有关。新的代码不会迭代一个linq到SQL生成列表,同时使另一个linq进入sql调用。它现在使用一个简单的for循环索引计数器。 – user957902 2012-03-29 21:45:58

使用一个临时变量来存储迭代值:

foreach (LINQ.session sesson in currentSessions) 
{ 
    var tempSession = session; // now use tempSession 
    .... 

这是迭代值的闭合的已知副作用。

+0

刚刚尝试过......相同的结果。 – 2012-03-29 15:17:41

+0

@LanceIngle:那么'sesson'的_only_引用是什么时候分配给'tempSession'? (我看到3) – 2012-03-29 15:20:41

+0

你确定你替换了所有引用的会话吗? – 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}); 

鉴于此,您可以提供一个工作的例子,是不依赖于任何本地资源?

+0

我得到它的工作,我不明白它为什么它工作,虽然我添加了一个新的类,循环虽然我的LINQ到SQL结果,并创建了我的新类的列表。我的for循环和一切都很好。我能想到的唯一的事情是LINQ到SQL列表不是线程安全的吗? – 2012-03-29 17:37:51

+0

你可能已经循环了关于你隐藏的东西的闭合变量,你必须显示该代码让我们知道肯定。 – 2012-03-29 17:59:23

+0

我在解决方案中发布了上述所有内容(全功能)。 – 2012-03-29 18:09:56