2016-03-03 4 views
0

Как исправить код, который каждый новый поток использует новый счетчик. В тот момент, когда вы начинаете новый поток, висит старый, а не продолжается.Резьба и новый счетчик

Спасибо за помощь.

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

Привет, Пожалуйста, не искажать свой пост после того, как вы приняли помощь от него. Это похоже на вырубку дерева после укрытия под ним. Пожалуйста, позвольте другим будущим пользователям получить знания. Ответчики приложили бы немало усилий. Не тратьте свое драгоценное время впустую. –

ответ

3

Проблема в том, что вы увеличиваете переменную 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); 
    } 
} 

Это делает все инициализации вещей в вашем случае кнопки и переходит в экземпляре информационного класса. Этот информационный класс принимает всю информацию, необходимую потоку, но так, чтобы она была локальной для потока!

+0

Спасибо за ваш комментарий к моему ответу. Можете ли вы прокомментировать, если имеет смысл делать то, что он сделал в фоновом потоке? Я имею в виду, что он хочет перемещать изображение в фоновом потоке. Он меняет координаты на фоновом потоке, а для изменения местоположения изображения в пользовательском интерфейсе он должен вернуться в основной поток назад. – Sasha

+0

Ну, я не могу сказать, имеет ли смысл, поскольку это его проект. Но в любом реагирующем приложении у вас есть работа, работающая в фоновом режиме, и вам в какой-то момент нужно обновить интерфейс, так что вообще-то, действительно, имеет смысл обновить материал пользовательского интерфейса из фоновых потоков. Если это полезно в этом случае или же то же самое можно было бы достичь с помощью таймеров, я не могу сказать. –

0

С кодом возникает несколько проблем.

  • Вы используете переменную 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()); 
} 
3

Ваша старая нить не висит. Проблема заключается в вашей переменной счетчика. Он поделился с вами потоками. Старый поток просто продолжается на kolejka[counter] нового потока. Наверное, это не то, что вы хотите.

В начале вашего метода goThread вы можете сделать что-то вроде:

var item = kolejka[counter]; 

И тогда вы можете использовать элемент вместо kolejka [счетчик]. Однако это еще не потокобезопасность, но намного лучше, чем у вас сейчас.

+0

Делает ли какой-либо смысл создание элемента пользовательского интерфейса в другом потоке? Я имею в виду, что для создания в фоновом потоке для одного элемента управления PictureBox с несколькими свойствами должно быть накладные расходы, а затем вернуться в основной поток, чтобы нарисовать его. – Sasha

+0

Переменная 'counter' может быть увеличена при запуске/запуске потока. Вы должны создать Событие, чтобы ждать. –

0

Развейте Питер, вы можете создать копию в начале резьбы:

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