传递给ParameterizedThreadStart的对象被覆盖?

时间:2012-03-29 15:09:34

标签: c# multithreading

背景

我正在开发一个小应用程序,它将通过WMI远程读取事件日志中的事件。基本上我正在寻找工作站锁定和解锁的时间。

问题:

我创建了一个线程数组。我遍历我的数据集(计算机名)并启动多个 带有自定义对象的ParameterizedThreadStart对象(LockHunterArgs)。问题是我知道我的数据集中没有重复项。我在一个线程函数的末尾添加了一个console.writeline,它显示了重复项。

此外,在我尝试使用线程之前。如果我同步运行代码它运行正常。这花了很长时间。所以这就是我试图引入多线程的原因。

示例输出:

// ...在

上面剪了一些独特的线条

计算机:COMP时间:2012/3/29 8:05:11会议:3935dd76-6a10-41a9-bd96-86143c66482d 电脑:COMP时间:2012/3/29 8:05:11会议:3935dd76-6a10-41a9-bd96-86143c66482d

// ...在

下面剪下一些独特且重复的行

我的假设:

如果我在get_lock_data函数的前几行放置一个断点,它正在转换并跳到下一行。这是随机的。它会前进一次然后两次击中同一行。我甚至看到它往下走了两行然后倒退了。我认为这是因为我正在解雇线程并且它在不同的时间击中了点,给出了它倒退的错觉。但它几乎就像被传入的对象被后来的线程覆盖了。

我尝试创建另一个LockHunterArgs数组,并在线程触发过程中创建和分配它们,但这也没有用。

这可能是愚蠢的。提前谢谢。

// lance

代码:

    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>();

    }

2 个答案:

答案 0 :(得分:0)

使用temp变量存储迭代值:

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

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

答案 1 :(得分:0)

我会说问题最有可能发生在你剪掉的东西上。我无法使用这些伪造的数据重现您的问题:

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});

鉴于此,您能否提供一个不依赖于任何本地资源的工作示例?