线程和新计数器

Thread and new counter

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

感谢您的帮助。

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

您的代码有几个问题。

  • 您在两个线程中使用一个变量 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());
}

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

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

var item = kolejka[counter];

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

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

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

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

跟进 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);
    }
}