我正在使用线程的项目。在某些情况下,我有以下问题:

这是我的一些代码:

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个项目,则此代码必须将Login过程调用3次。但每次登录过程都将使用相同的用户名和密码。因此,我所有的电子邮件都无法正确登录。
如果我删除线程池,就可以了。

最佳答案

您的代码非常困惑:

  • 如果在代码中添加lock,它将同步运行,一次仅运行一个线程,这将导致性能下降。
  • 如果您通过QueueUserWorkItem排队工作-它会在其他线程中运行,并且不在TicketLock
  • 您应该将锁封装在类内部,并且不应在程序中锁定整个逻辑。
  • 您将开始使用循环变量i,即being closured for it's last value,它会导致您在最后一句中指出的问题。
  • lock类中的
  • Email对象不是static,因此它是为每个实例创建的,实际上并没有锁定。
  • 当您使用Invoke方法时,您的代码是从UI启动的,您需要传递同步上下文。我建议您为此使用 TPL code,而不是直接与 ThreadPool 一起使用

  • 因此,我建议您采用以下解决方案:
    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;
                }
            }
    
        }
    }
    

    10-05 20:47
    查看更多