C#线程:在错误对象上执行的线程

C#线程:在错误对象上执行的线程,c#,multithreading,C#,Multithreading,也许这是我犯的一个愚蠢的错误,但我无法理解 所以这个场景是我每隔200毫秒从数据库中检索一条记录,如果有一条记录可用的话。 在每条记录上,我开始一个线程。 在这种情况下,它是发送的邮件 QMail mailRecord; while (!stopSending) { if (QueueMailingHandler.m_numActive >= MaxThreads) { Thread.Sleep(2000)

也许这是我犯的一个愚蠢的错误,但我无法理解

所以这个场景是我每隔200毫秒从数据库中检索一条记录,如果有一条记录可用的话。 在每条记录上,我开始一个线程。 在这种情况下,它是发送的邮件

    QMail mailRecord;

    while (!stopSending)
    {

        if (QueueMailingHandler.m_numActive >= MaxThreads)
        {
            Thread.Sleep(2000);
            continue;
        }

        mailRecord = QMail.Next();

        if (mailRecord.UID > 0)
        {
            QueueMailingHandler.m_numActive++;

            QueueMailingHandler MailingHandler = new QueueMailingHandler();

            mailRecord.Processing = true;

            MailingHandler.Start(mailRecord);
        }

        Thread.Sleep(200);
    }
我的邮件管理员:

internal QueueMailingHandler()
{
}

internal void Start(QMail rec)
{
    record = rec;
    Thread thread = new Thread(new ThreadStart(ProcessThread));

    thread.IsBackground = true;

    thread.Start();
}

public void ProcessThread()
{
    _logging = new AutoQueueLog(record.UID.ToString(), "Sending Mails", record.Subject, "Processing");
    _logging.Path = @"C:\Windows Services\QueueMailing\AutoLog";
    _logging.LogMessage();
    try
    {

        SendMail(record);
        record.SetDone();
        _logging.State = "Done";
        _logging.LogMessage();
    }
    catch (Exception ex)
    {
        _logging.State = "Error";
        _logging.LogException = ex;
        _logging.Level = AutoLog.ExceptionLevel.Major;
        _logging.LogMessage();
    }
    finally
    {
        m_numActive--;
    }
}
作为记录结果,我得到:

6/16/2015 11:57:02 AM - [328] - Function : QueueMailingHandler.ProcessThread() - Processing
6/16/2015 11:57:02 AM - [329] - Function : QueueMailingHandler.ProcessThread() - Processing
6/16/2015 11:57:02 AM - [329] - Function : QueueMailingHandler.ProcessThread() - Done
6/16/2015 11:57:02 AM - [330] - Function : QueueMailingHandler.ProcessThread() - Done
6/16/2015 11:57:02 AM - [330] - Function : QueueMailingHandler.ProcessThread() - Processing
6/16/2015 11:57:02 AM - [331] - Function : QueueMailingHandler.ProcessThread() - Processing
6/16/2015 11:57:02 AM - [331] - Function : QueueMailingHandler.ProcessThread() - Done
6/16/2015 11:57:03 AM - [332] - Function : QueueMailingHandler.ProcessThread() - Processing
6/16/2015 11:57:03 AM - [333] - Function : QueueMailingHandler.ProcessThread() - Processing
6/16/2015 11:57:03 AM - [333] - Function : QueueMailingHandler.ProcessThread() - Done
6/16/2015 11:57:03 AM - [333] - Function : QueueMailingHandler.ProcessThread() - Done
6/16/2015 11:57:03 AM - [333] - Function : QueueMailingHandler.ProcessThread() - Done
问题是:为什么我的线程使用的对象应该在一个单独(但类似)的线程中

欢迎任何帮助。谢谢

而不是这个

 QMail mailRecord;

while (!stopSending)
{

    if (QueueMailingHandler.m_numActive >= MaxThreads)
    {
        Thread.Sleep(2000);
        continue;
    }

    mailRecord = QMail.Next();

    if (mailRecord.UID > 0)
    {
        QueueMailingHandler.m_numActive++;

        QueueMailingHandler MailingHandler = new QueueMailingHandler();

        mailRecord.Processing = true;

        MailingHandler.Start(mailRecord);
    }

    Thread.Sleep(200);
}
试试这个

while (!stopSending)
{

    if (QueueMailingHandler.m_numActive >= MaxThreads)
    {
        Thread.Sleep(2000);
        continue;
    }

    QMail mailRecord = QMail.Next();

    if (mailRecord.UID > 0)
    {
        QueueMailingHandler.m_numActive++;

        QueueMailingHandler MailingHandler = new QueueMailingHandler();

        mailRecord.Processing = true;

        MailingHandler.Start(mailRecord);
    }

    Thread.Sleep(200);
}

这里唯一的区别是声明QMail实例的位置。

这是因为所有线程都使用相同的
record
共享实例

解决此问题的简单方法是在启动线程时使用参数:

internal void Start(QMail rec)
{
    Thread thread = new Thread(new ParameterizedThreadStart(ProcessThread));

    thread.IsBackground = true;

    thread.Start(rec);
}
然后,在线程中,使用参数而不是属性:

public void ProcessThread(object parameter)
{
    var record = (QMail)parameter;

    _logging = new AutoQueueLog(record.UID.ToString(), "Sending Mails", record.Subject, "Processing");
    _logging.Path = @"C:\Windows Services\QueueMailing\AutoLog";
    _logging.LogMessage();
    try
    {

        SendMail(record);
        record.SetDone();
        _logging.State = "Done";
        _logging.LogMessage();
    }
    catch (Exception ex)
    {
        _logging.State = "Error";
        _logging.LogException = ex;
        _logging.Level = AutoLog.ExceptionLevel.Major;
        _logging.LogMessage();
    }
    finally
    {
        m_numActive--;
    }
}
此外,以这种方式减少
m_numActive
计数器将产生不可预测的结果。您应该使用线程安全的方法,例如
互锁.减量
方法:

Interlocked.Decrement(ref m_numActive);

最后但并非最不重要的是,您应该考虑使用线程池,而不是每次创建一个新线程:

ThreadPool.QueueUserWorkItem(ProcessThread, rec);

那么你的问题是什么?我很乐意帮忙,但你的问题到底是什么?我几乎可以肯定,错误在你的
record=rec行。你想复制你的对象,但你只是复制一个对原始对象的引用,所以下一个
record=rec将覆盖最后一个。你能试着打电话给
rec
上的复制管理员吗?这不是对你问题的回答,但线程的级别确实很低。大多数情况下,最好使用一些更高级别的构造,如tasks或async and Wait。特别是,默认情况下,32位进程中的每个线程将为堆栈消耗1MB的虚拟地址空间,因此一千个线程将消耗1GB的虚拟地址空间。在为操作系统预留2GB的情况下,您可以很容易地耗尽内存。在您的情况下,您的“记录”变量不是线程安全的,这会导致意外行为。记录对象是从所有活动线程引用的。了解线程安全性。好吧,这个Start()函数似乎正在发挥作用。但是你建议我使用线程池而不是这里的线程。什么是冒险?互锁的.Increment/decreation仍然有效吗?@Devcon2线程池是一组由.NET运行时直接管理的线程。它适用于主动创建线程之前的短任务(从而在启动任务时节省创建延迟),并且活动线程的数量也适用于最适合机器资源的情况。Interlocked.Increment/Decrement只是处理数字的线程安全方法,所以它不受您是手动创建线程还是使用线程池的影响