【问题标题】:Making objects move using threads使用线程使对象移动
【发布时间】:2018-11-03 23:11:20
【问题描述】:

每次点击pictureBox 时,我都会在列表中添加Car 类型的对象。 我想让它们一点击picutreBox就移动。

这是我的代码

private Thread t;
private void pictureBox1_MouseClick_1(object sender, MouseEventArgs e)
{                
    list.Add(new Car(e.X, e.Y, 40, 40));
    br++;
    t = new Thread(Draw);
    t.Start();  
}

画:

private void Draw()
{
    Graphics g = pictureBox1.CreateGraphics();           
    for(int i = 0; i < 1000; i++)
    {
        list[br].DrawCar(g, Color.Red);
        list[br].Move();               
        Thread.Sleep(100);
        pictureBox1.Invoke(
            (MethodInvoker)delegate
            {
                pictureBox1.Refresh();
            });
    }            
    g.Dispose();
}

我遇到的问题如下:

  1. 当我第一次点击时,它正在很好地移动,但是当我再次点击时,汽车停下来,下一辆车开始移动。

  2. 我点击表格越多,车开得越快,我不知道为什么

  3. 当我退出表单时,我得到一个异常:

System.InvalidOperationException: 'Invoke 或 BeginInvoke 不能 在创建窗口句柄之前调用控件。'

【问题讨论】:

  • 任何涉及显示的事情都只能发生在 UI 线程中(这就是您需要 Invoke 的原因)。那么,为什么要使用线程?另外:您使用Graphics g = pictureBox1.CreateGraphics(); 绘制的任何内容都不会保留。更多错误可能在Move代码中,但我们看不到。
  • 小格式类型,&lt;br&gt; 在此站点上没有任何作用。如果你想换行,你必须连续输入两次。
  • 每次打电话new Thread(...)都会有一个小疯子。创建线程的计算成本很高,占用 1MB 的 RAM,而且速度很慢。只有当您即将执行 CPU 密集型操作时,优势才会出现。您的代码似乎并非如此。

标签: c# multithreading picturebox


【解决方案1】:
  1. 第一辆汽车停止,因为 br 增加,Draw 函数现在移动列表中的第二辆汽车。
  2. 如果你多次点击图片框,将会有多个线程运行Draw函数,它们都在移动同一辆车(见1)
  3. 当窗体关闭并且线程仍在运行时,该线程会尝试访问图片框。它可能已经不存在了。

解决方案

  1. 将 br 设为局部变量并将 br 作为参数传递给 thread.Start() 方法。
  2. 应由 1 修复。
  3. 在表单的 OnClosing 中停止所有线程

【讨论】:

    【解决方案2】:

    我在您的代码中看到了几个危险信号。

    每次都创建一个新线程。旧线程会发生什么?另外创建一个新线程来移动每辆车也不理想。

    使用 br 来索引列表中要移动和绘制的汽车。当你添加第二辆车时,br 现在是 1,所以现在第一个 Draw 线程将使用 list[1]。现在你有 2 个线程都在移动第二辆车。这就是为什么第一辆车停止移动而第二辆车开始快速移动的原因。

    调用图片框刷新也不理想。

    我会更喜欢这样:

    List<System.Windows.Forms.Timer> timers =
      new List<System.Windows.Forms.Timer>();
    List<Car> list = new List<Car>();
    
    private void button1_Click(object sender, EventArgs e)
    {
      Car car = new Car(50, 50, 40, 40);
      list.Add(car);
      Timer timer = new Timer();
      timer.Tick += Timer_Tick;
      timer.Interval = 100;
      timer.Tag = car;
      timer.Start();
      timers.Add(timer);
    }
    
    private void Timer_Tick(object sender, EventArgs e)
    {
      Car car = (Car)((sender as Timer).Tag);
      car.Move();
      pictureBox1.Invalidate();
    }
    
    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
      Graphics g = e.Graphics;
      g.Clear(Color.White);
    
      foreach (Car car in list.ToArray())
      {
        car.DrawCar(g, Color.Blue);
      }
    }
    

    car.Move 和 DrawCar 中可能还有其他问题。

    【讨论】:

    • 这很危险,因为列表在迭代时可能会被修改。应该使用锁来保护集合不受此影响。
    猜你喜欢
    • 2015-03-18
    • 1970-01-01
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多