同时运行方法

时间:2015-10-07 04:13:51

标签: c# multithreading

我有一个带有方法Run的Dog类,它应该在屏幕上移动图片:

let

从按钮单击事件调用此方法,其中创建了4个狗对象,并且每个对象都调用Run方法:

public bool Run()
{
    Point p = PictureBoxDog.Location;

    while(p.X < 530)
    {
        int movement = Randomizer.Next(0, 3);
        p.X += movement;
        PictureBoxDog.Location = p;
    }

    if (Location == 4) //Incomplete section.
        return true;
    else
        return false;
}

问题是每个方法一个接一个地执行,而不是同时执行。我希望每个方法同时运行。如果我删除了while语句,那么所有方法都会同时执行,但是使用while循环,它们会一个接一个地执行。任何关于如何解决这个问题的建议都非常感谢。没有while循环的run方法:

private void button1_Click(object sender, EventArgs e)
{
    Dog dog1 = new Dog(pictureDog1);
    Dog dog2 = new Dog(pictureDog2);
    Dog dog3 = new Dog(pictureDog3);
    Dog dog4 = new Dog(pictureDog4);

    dog1.Run();
    dog2.Run();
    dog3.Run();
    dog4.Run();
}

3 个答案:

答案 0 :(得分:13)

动画和WinForms通常并不简单。程序员通常做的是建立一个游戏循环。游戏循环执行三项操作 - 获取用户输入,更新精灵的新位置,然后在屏幕上绘制精灵。

using System.Threading;

public partial class Form1
{ 
   private Timer _timer;
   private Dog _dog1, _dog2, _dog3, _dog4;

   public void InitializeComponent()
   {
      SetupDogs();

      // Every quarter of a second, run the function GameLoop
      _timer = new Timer(GameLoop, null, 
        TimeSpan.FromSeconds(0.25),
        TimeSpan.FromSeconds(0.25));
   }

   private void SetupDogs()
   {
      _dog1 = new Dog(PictureBoxDog1);
      _dog2 = new Dog(PictureBoxDog2);
      _dog3 = new Dog(PictureBoxDog3);
      _dog4 = new Dog(PictureBoxDog4);

   }

   public void GameLoop(object state)
   {
       GetUserInput();
       Update();
       Draw();
   }

   public void GetUserInput()
   {
     // You don't need this now. But if you need to
     // process user input later, you can do it here.
     //
     // e.g. if Last key 
     //   pressed  was arrow-left or 
     //   arrow-right etc.
   }

   public void Update()
   {
     _dog1.Update();
     _dog2.Update();
     _dog3.Update();
     _dog4.Update();
   }

   public void Draw()
   {
      // Draw on the main UI thread
      Dispatcher.BeginInvoke(() => 
      {
         _dog1.Draw();
         _dog2.Draw();
         _dog3.Draw();
         _dog4.Draw();
      });
   }

}

然后你的Dog课程看起来像这样。每次计时器滴答时,它需要Update其位置,然后绘制其位置:

public class Dog
{

  bool _isRunning = true;

  Point Location { get; set; }

  Point NextLocation { get; set; }

  PictureBox PictureBoxDog { get; set; }

  public Dog(PictureBox pictureBox) 
  {
     PictureBoxDog = pictureBox;

     Location = GetRandomLocation();

     NextLocation = GetRandomLocation();
  }

  private Point GetRandomLocation()
  {
     var random = new Random();
     return new Point(random.Next(800), random.Next(800));
  }

  public void Update()
  {
    // Calculates the new coordinates for a dog

    // The dog starts from a random position, and is 
    // given a new random position to run towards.

    // If the dog has arrived at the new random position, then
    // give the dog a new random position to run to again
    if (NextLocation.X == Location.X && NextLocation.Y == Location.Y)
    {
      NextLocation = GetRandomLocation();
    }

    if (_isRunning)
    {
       // Move the dog closer to its destination
       // dx and dy can be -1, 0, or 1
       var dx = Math.Sign(NextLocation.X - Location.X);
       var dy = Math.Sign(NextLocation.Y - Location.Y);

       Location = new Point(Location.X + dx, Location.Y + dy);
    }
  }

  public void Draw()
  {
     PictureBoxDog.Location = Location;
  }
}

答案 1 :(得分:4)

试试这个

Dispatcher.BeginInvoke((Action) (() =>
{
     dog1.Run();
}));
Dispatcher.BeginInvoke((Action) (() =>
{
     dog2.Run();
}));
Dispatcher.BeginInvoke((Action) (() =>
{
     dog3.Run();
}));
Dispatcher.BeginInvoke((Action) (() =>
{
     dog4.Run();
}));

和While循环也使用此

Dispatcher.BeginInvoke((Action) (() =>
{
    while(p.X < 530)
    {
        int movement = Randomizer.Next(0, 3);
        p.X += movement;
        PictureBoxDog.Location = p;
    }
}));

BeginInvoke是异步的;因此,控制在调用后立即返回调用对象。 BeginInvoke返回一个DispatcherOperation对象,当委托在事件队列中时,该对象可用于与委托进行交互。 BeginInvoke返回的DispatcherOperation对象可以通过多种方式与指定的委托进行交互

答案 2 :(得分:0)

为什么不尝试这个?

Task.Factory.StartNew( () => Parallel.ForEach<Dog>(Dogs, dog=> dog.run()));

要正确使用此指令,您应该创建一个Dog列表。

 List<Dog> Dogs = new List<Dog>();
        Dogs.Add(dog1);
        Dogs.Add(dog2);
        Dogs.Add(dog3);
        Dogs.Add(dog4);
        Task.Factory.StartNew(() => Parallel.ForEach<Dog>(Dogs, dog => dog.Run()));

如果您不喜欢列表:

Task.Factory.StartNew(() => dog1.Run());
Task.Factory.StartNew(() => dog2.Run());
Task.Factory.StartNew(() => dog3.Run());
Task.Factory.StartNew(() => dog4.Run());

要从不同的线程与UI进行交互,您需要使用委托并调用Control.Invoke / BeginInvoke。 您可以使用InvokeRequired属性测试是否需要调用Invoke。 所以在你的情况下:

if (PictureBoxDog.InvokeRequired){
PictureBoxDog.Invoke((MethodInvoker)(() => PictureBoxDog.location = p));}
相关问题