【问题标题】:Thread and new counter线程和新计数器
【发布时间】:2016-03-03 09:30:37
【问题描述】:

如何更正每个新启动的线程使用新计数器的代码。在您启动新线程的那一刻,旧线程挂起,而不是继续。

感谢您的帮助。

 private void button1_Click(object sender, EventArgs e)
    {

        thread[counter] = new Thread(goThread);
        thread[counter].Start();
        counter++; 
    }

    private void goThread()
    {
            kolejka[counter] = new PictureBox();
            kolejka[counter].Location = new Point(325, n - 150);
            kolejka[counter].Image = threading.Properties.Resources.car;
            kolejka[counter].Size = new Size(20, 37);

        this.Invoke((MethodInvoker)delegate
        {

            this.Controls.Add(kolejka[counter]);
        });


        for (int i = 0; i < 500; i++)
        {
            this.Invoke((MethodInvoker)delegate
            {
                kolejka[counter].Location = new Point(kolejka[counter].Location.X, kolejka[counter].Location.Y - 3);
                this.Refresh();
            });
            Thread.Sleep(50);
        }
    } 

【问题讨论】:

  • 您好,在您获得帮助后,请不要破坏您的帖子。这就像在树下避难后砍倒一棵树。请允许其他未来的用户从知识中获益。回答者会付出很多努力。不要浪费他们宝贵的时间。

标签: c# multithreading counter


【解决方案1】:

问题是您增加了counter 变量,但它在您的线程中使用。不要那样做。在您的情况下,将信息设为线程本地非常重要,因为您希望每个线程都在“自己的”计数器上工作。可以这样实现:

private class ThreadInfo
{
    public PictureBox Picture;
    public int Counter;
}

private void button1_Click(object sender, EventArgs e)
{
    kolejka[counter] = new PictureBox();
    kolejka[counter].Location = new Point(325, n - 150);
    kolejka[counter].Image = threading.Properties.Resources.car;
    kolejka[counter].Size = new Size(20, 37);
    this.Controls.Add(kolejka[counter]);

    ThreadInfo info = new ThreadInfo() {
        Picture = kolejka[counter],
        Counter = counter
    };

    thread[counter] = new Thread(goThread);
    thread[counter].Start(info);

    counter++; 
}

private void goThread(object state)
{
    ThreadInfo info = state as ThreadInfo;

    for (int i = 0; i < 500; i++)
    {
        this.Invoke((MethodInvoker)delegate
        {
            info.Picture.Location = new Point(info.Picture.Location.X, info.Picture.Location.Y - 3);
            this.Refresh();
        });
        Thread.Sleep(50);
    }
} 

这会在您的按钮事件中完成所有初始化工作,并传入一个信息类的实例。该信息类获取线程所需的所有信息,但它是线程本地的!

【讨论】:

  • 感谢您对我的回答的评论。你能否发表评论,如果它有做他在后台线程上所做的事情吗?我的意思是他想在后台线程中移动图像。他在后台线程上更改坐标,并且要更改 UI 上的图片位置,他应该再次返回主线程。
  • 好吧,我不能确定这是否有意义,因为这是他的项目。但是在任何响应式应用程序中,您都有在后台运行的东西,并且您有时需要更新 UI,所以一般来说,是的,从后台线程更新 UI 内容确实有意义。如果在这种情况下有用,或者是否可以使用计时器来实现同样的事情,我不能说。
【解决方案2】:

您的旧线程没有挂起。问题出在您的计数器变量中。它由您的线程共享。旧线程只是在新线程的kolejka[counter] 上继续。我想这不是你想要的。

在 goThread 方法的开头,您可以执行以下操作:

var item = kolejka[counter];

然后你可以使用 item 代替 kolejka[counter]。然而,这也不是线程安全的,但比你现在拥有的要好得多。

【讨论】:

  • 在另一个线程上创建 UI 元素是否有意义?我的意思是在后台线程中为一个具有少量属性的 PictureBox 控件创建开销,然后返回主线程来绘制它
  • counter 变量可能会在线程启动/运行时增加。你应该创建一个事件来等待。
【解决方案3】:

您的代码有几个问题。

  • 您使用变量counter 而不锁定在两个线程中。
  • 不要为此使用数组,因为您无法控制 counter 值。
  • 不要在除 gui 线程之外的其他线程上创建控件。

为此,您不需要线程。最简单的方法是使用一个计时器。

伪:

List<Car> _myCars = new List<Car>();

private Form1()
{
    _timer = new Timer();
    _timer.Interval = 50;
    _timer.Tick += Timer_Tick;
}

private void Timer_Tick(object sender, EventArgs e)
{
    foreach(var car in _myCars.ToArray())
    {
        car.Location = new Point(car.Location.X, car.Location.Y - 3);
        if(car.Location.Y < 0)
            _myCars.Remove(car);
    }
}

private void button1_Click(object sender, EventArgs e)
{
    _myCars.Add(new Car());
}

【讨论】:

    【解决方案4】:

    跟进Peter,您可以在帖子开头创建一个副本:

    private void button1_Click(object sender, EventArgs e)
    {
        ManualResetEvent threadStartedSignal = new ManualResetEvent(false);
        thread[counter] = new Thread(goThread);
        thread[counter].Start(threadStartedSignal);
    
        // wait for the thread to create a local reference.
        threadStartedSignal.WaitOne();
        counter++; 
    }
    
    private void goThread(object state)
    {
        kolejka[counter] = new PictureBox();
        var myPictureBox = kolejka[counter];
    
        // signal the other thread, that the counter may be changed.
        ((ManualResetEvent)state).Set();
    
        myPictureBox .Location = new Point(325, n - 150);
        myPictureBox .Image = threading.Properties.Resources.car;
        myPictureBox .Size = new Size(20, 37);
    
        this.Invoke((MethodInvoker)delegate
        {
            this.Controls.Add(myPictureBox );
        });
    
    
        for (int i = 0; i < 500; i++)
        {
            this.Invoke((MethodInvoker)delegate
            {
                myPictureBox.Location = new Point(myPictureBox.Location.X, myPictureBox.Location.Y - 3);
                this.Refresh();
            });
            Thread.Sleep(50);
        }
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-06
      • 2013-12-21
      • 2017-02-25
      • 1970-01-01
      • 2020-11-17
      • 2015-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多