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

Threads conflict in some cases, in loop conditions

我正在从事一个使用线程的项目。在某些情况下,我遇到了这些问题:

这是我的部分代码:

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

                    }));
                });
            }
        }
    }
}

这是电子邮件地址 class :

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

    }
}

我的问题是:

当我想使用属于EmailAddressClass的Login过程时,它有一些冲突。如您所见,我使用了 Lock 但任何东西都变了。

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

将您的代码更改为并尝试。您的代码正在从 lstEmailAddress 中排队,它将始终到达列表中的最后一项。更改您的代码以查询线程池中的每个项目。那应该解决。它。

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

你的代码很混乱:

  1. 如果在代码中加入lock,会运行同步,一次只有一个线程,会导致性能损失。
  2. 如果您通过 QueueUserWorkItem 排队工作 - 它将 运行 在其他线程中,并且 不在 TicketLock
  3. 您应该将锁封装在您的 class 中,而不应该在您的程序中锁定整个逻辑。
  4. 你开始为循环变量 i 工作,它是 being closured for it's last value,它导致你在最后一句话中陈述的问题。
  5. lock Email class 中的对象不是 static 所以它是为每个实例创建的,实际上并没有锁定任何东西。
  6. 由于您使用的是 Invoke 方法,您的代码是从 UI 开始的,您需要传递同步上下文。我建议你使用 TPL code for this, and do not directly work with 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;
            }
        }

    }
}