并发队列未出列/清除

时间:2017-06-26 14:52:11

标签: c# multithreading winforms

我的要求是在队列中插入项目并对其进行处理,但应首先添加项目,并在一段时间后处理它们(因为在处理项目之前需要设置其他一些内容)这是我到目前为止所做的编码。

    #region Variables Declarations

    private Thread threadTask = null;

    ConcurrentQueue<string> concurrentQueue = new ConcurrentQueue<string>();     
    string currentSeqNo;
    string previousSeqNo = "-1";        

    #endregion

    private void test1_Load(object sender, EventArgs e)
    {
        AddItems();           

        if (threadTask == null)
        {
            threadTask = new Thread(Kick);
            Thread.Sleep(5000);               
            threadTask.Start();
        }
    }

    private void AddItems()
    {
        for (Int64 i = 100000; i < 300000; i++)
        {                
            concurrentQueue.Enqueue(i.ToString());
            this.Invoke(new MethodInvoker(delegate()
            {                    
                label1.Text = i.ToString();
                label1.Update();                   
            }));
        }
    }

    private void Kick()
    {
        while (true)
        {
            int recordCountNew = concurrentQueue.Count();
            if (recordCountNew != 0)
            {
                RemoveItems();
            }
        }
    }

    private void RemoveItems()
    {
        string item;

        while (concurrentQueue.TryDequeue(out item))
        {
            this.Invoke(new MethodInvoker(delegate()
            {                   
                label2.Text = item;
                label2.Update();
            }));

            currentSeqNo = item;    // second time does not start wil 100000

            if (previousSeqNo != "-1")
            {
                if (long.Parse(currentSeqNo) != long.Parse(previousSeqNo) + 1)
                {
                    Reconnect();
                }
                else
                {
                    //Process item
                    previousSeqNo = currentSeqNo;
                }
            }
            else
            {
                //Process item
                previousSeqNo = currentSeqNo;
            }
        }
    }

    private void Reconnect()
    {            
        currentSeqNo = "";
        previousSeqNo = "-1";

        string someItem;
        while (concurrentQueue.Count > 0)
        {
            concurrentQueue.TryDequeue(out someItem);
        }

        this.Invoke(new MethodInvoker(delegate()
        {
            label1.Text = "";
            label2.Text = "";

            label1.Update();
            label2.Update();
        }));

        AddItems();

        if (threadTask == null)
        {
            threadTask = new Thread(Kick);                
            threadTask.Start();
        }
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
        Reconnect();
    }

要重现此问题:运行应用程序并在中间单击按钮。现在队列应该再次从100000开始,但它显示的数字大于100000.

请告知我如何在点击按钮后释放所有资源以重新启动。虽然我将它们设置为默认值并清除队列但是当&#39; RemoveItems&#39;时,它仍会在currentSeqNo中显示旧值。方法被调用。

3 个答案:

答案 0 :(得分:0)

几点意见:

1)你Kick()方法有一个无限循环,也没有睡眠。每个启动的线程都会继续运行,因为你没有线程的范围。

您可以拥有bKeepRunning之类的成员变量,默认值为true。在false函数的开头将该变量设置为Reconnect()。类似的东西:

private void Kick()
{
    while (bKeepRunning)
    {
        int recordCountNew = concurrentQueue.Count();
        if (recordCountNew != 0)
        {
            RemoveItems();
        }
    }
}
  1. 为什么Thread.Sleep(5000);中有test1_Load()?我不认为这是必要的。
  2. 我对您的代码做了一些小改动,例如:

    private void AddItems()
    {
        for (Int64 i = 100000; i < 300000; i++)
        {
            concurrentQueue.Enqueue(i.ToString());
    
            this.Invoke(new MethodInvoker(delegate()
            {
                label1.Text = i.ToString();
                label1.Update();
            }));
    
            if (i < 100004)
                Thread.Sleep(1000);
        }
    }
    
    private void Kick()
    {
        while (bKeepRunning)
        {
            int recordCountNew = concurrentQueue.Count();
            if (recordCountNew != 0)
            {
                RemoveItems();
            }
        }
    } 
    
    private void Reconnect()
    {
        currentSeqNo = "";
        previousSeqNo = "-1";
        bKeepRunning = false;
        threadTask = null;
    
        string someItem;
        while (concurrentQueue.Count > 0)
        {
        concurrentQueue.TryDequeue(out someItem);
        }
    
        this.Invoke(new MethodInvoker(delegate()
        {
        label1.Text = "";
        label2.Text = "";
    
        label1.Update();
        label2.Update();
        }));
        Thread.Sleep(2000);
    
        AddItems();
    
        bKeepRunning = true;
    
    
        if (threadTask == null)
        {
        threadTask = new Thread(Kick);
        threadTask.Start();
        }
    }
    

    它帮助我看到价值从100000开始。您可以尝试相同的结果。

    注意:我已停止线程并在单击按钮后重新启动。因此,我不会在您的代码中看到任何缺陷。它只是快速运行,因此您无法实现起始值。

答案 1 :(得分:0)

你看到的是Kick线程和按钮点击处理程序之间的竞争条件。当您按下按钮时,在其中执行Reconnect(),您清理队列,然后调用AddItems()函数。但是所有这一次Kick函数都会尝试Dequeue,因此每次都会有一个任意数量的项目。您应该做的是在这些功能之间进行同步,或者在添加项目时阻止Kick执行。

答案 2 :(得分:0)

您应该使 UI线程 threadTask线程同步,只需使用 ManualResetEventSlim 信号构造,就像这样:

static ManualResetEventSlim guard = new ManualResetEventSlim(true);
private void button1_Click_1(object sender, EventArgs e)
{
    guard.Reset();
    Reconnect();
    guard.Set();
}
private void RemoveItems()
{
    string item;

    while (concurrentQueue.TryDequeue(out item))
    {
        guard.Wait();
        //......
     }
}

请参阅:

  

ManualResetEventSlim Class