在某些情况下,在循环条件下,线程会发生冲突

时间:2015-04-16 07:28:26

标签: c# multithreading threadpool

我正在开发一个使用Threads的项目。在某些情况下,我遇到了这些问题:

以下是我的一些代码:

List<EmailAddress> lstEmailAddress = new List<EmailAddress>();
private void TimerCheckInternetConnection_Tick(object sender, EventArgs e)
{
    lock (TicketLock)
    {
        if (UtilityManager.CheckForInternetConnection())
        {
            if (ApplicationRunStatus == Enum_ApplicationRunStatus.UnknownDisconnect || ApplicationRunStatus == Enum_ApplicationRunStatus.IsReady)
            {
                // Connect
                ThreadPool.QueueUserWorkItem((o) =>
                {
                    for (int i = 0; i < lstEmailAddress.Count; i++)
                    {
                        lstEmailAddress[i].IsActive = lstEmailAddress[i].Login();
                    }

                    this.BeginInvoke(new Action(() =>
                    {
                        // some code

                    }));
                });
            }
        }
    }
}

这是EmailAddress类:

class EmailAddress
{
    private Imap4Client imap = new Imap4Client();
    private object objectLock = new object();

    public bool IsActive;
    public string Address;
    public string Password;

    public string RecieveServerAddress;
    public int RecieveServerPort;

    public bool Login()
    {
        lock (objectLock)
        {
            try
            {
                imap.ConnectSsl(RecieveServerAddress, RecieveServerPort);
            }
            catch (Exception)
            {
            }

            try
            {
                imap.Login(Address, Password);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

    }
}

我的问题是:

当我想使用属于Login类的EmailAddress过程时,它有一些冲突。正如您所看到的,我使用了Lock,但任何事情都发生了变化。

更多详情:
如果我在lstEmailAddress中有3个项目,则必须通过此代码调用登录过程3次。但每次登录过程都将使用相同的用户名和密码。所以我的所有电子邮件都无法正确登录。 如果我删除线程池,它就没问题了。

2 个答案:

答案 0 :(得分:2)

您的代码非常混乱:

  1. 如果在代码中添加lock,它将同步运行,当时只有一个线程,这将导致性能下降。
  2. 如果您通过QueueUserWorkItem排队工作 - 它将在其他主题中运行,不在TicketLock
  3. 你应该在你的类中包含锁,并且不应该在你的程序中锁定整个逻辑。
  4. 你开始为一个循环变量i工作,这是being closured for it's last value,这导致你在最后一句中陈述的问题。
  5. {li> lock Email类中的对象不是static所以它是为每个实例创建的,并且实际上并不锁定anithing。
  6. 当您使用Invoke方法时,您的代码将从UI启动,您需要传递同步上下文。我建议您使用TPL code进行此操作,不要直接使用ThreadPool
  7. 所以我建议你这个解决方案:

    List<EmailAddress> lstEmailAddress = new List<EmailAddress>();
    private void TimerCheckInternetConnection_Tick(object sender, EventArgs e)
    {
        // remove this lock as we have another in Email class
        //lock (TicketLock)
        if (UtilityManager.CheckForInternetConnection())
        {
            if (ApplicationRunStatus == Enum_ApplicationRunStatus.UnknownDisconnect
              || ApplicationRunStatus == Enum_ApplicationRunStatus.IsReady)
            {
                for (int i = 0; i < lstEmailAddress.Count; i++)
                {
                    // use local variable to store index
                    int localIndex = i;
                    // Connect
                    ThreadPool.QueueUserWorkItem((o) =>
                    {
                        // if you add a lock here, this will run synchroniosly,
                        // and you aren't really need the ThreadPool
                        //lock (TicketLock)
                        lstEmailAddress[localIndex].IsActive = lstEmailAddress[localIndex].Login();
    
                        this.BeginInvoke(new Action(() =>
                        {
                            // some code
                        }));
                    });
                }
            }
        }
    }
    
    class EmailAddress
    {
        // if you have to login only for one user simultaneosly
        // use static variables here, other wise simply remove the lock as it is useless
        private static Imap4Client imap;
    
        private static object objectLock;
        // static constructor for only one initialization for a static fields
        static EmailAddress()
        {
            objectLock = new object();
            imap = new Imap4Client();
        }
    
        public bool IsActive;
        public string Address;
        public string Password;
    
        public string RecieveServerAddress;
        public int RecieveServerPort;
    
        public bool Login()
        {
            // aquire a static lock
            lock (objectLock)
            {
                try
                {
                    imap.ConnectSsl(RecieveServerAddress, RecieveServerPort);
                }
                catch (Exception)
                {
                    // STORE THE EXCEPTION!!!
                    // return as you haven't connected
                    return false;
                }
    
                try
                {
                    imap.Login(Address, Password);
                    return true;
                }
                catch (Exception)
                {
                    // STORE THE EXCEPTION!!!
                    return false;
                }
            }
    
        }
    }
    

答案 1 :(得分:-4)

将您的代码更改为并尝试。你的代码是来自lstEmailAddress的排队项目,它始终会从列表中找到最后一项。更改您的代码以查询threadpool中的每个项目。应该修复。它。

   for (int i = 0; i < lstEmailAddress.Count; i++)
     { 
    ThreadPool.QueueUserWorkItem((o) =>
                         {
                            lstEmailAddress[i].IsActive = lstEmailAddress[i].Login();
                         }   
    }