2012-12-06 29 views
5

Geliştirmeye çalıştığım form 6 resim kutusundan oluşan bir diziye ve 6 kalıptan oluşan bir görüntü dizisine sahip. Tıklandığında, her görüntüyü bir an için gösteren zarları "döndüren" 6 iş parçacığı oluşturması gereken bir düğmem var. Sorun şu ki, zar tıklatıldıktan sonra buton tıklatılarak bir yöntemi çağırmam gerekiyor. Zarın dönmesini sağlayabilirim, ancak mesaj kutusu hemen görüntülenir. Birkaç farklı yol denedim ve çeşitli hatalar aldım. Aşağıdaki çalışmayan sürümde, program donuyor. Bir tonluk kaynakları kontrol ettim ama Delegeler gibi bazı kavramları kavramak ve tüm bunları iyi bir şekilde çağırmak. Herhangi bir yardım harika olurdu! Burada görüntüleri ayarlama sizin çağırma ve resim kutuları Güncellemenize arasında bir kilitlenme alıyoruzMesaj dizilerim ile ilgili sorunlar

namespace testDice 
{ 
    public partial class Form1 : Form 
    { 
     private Image[] imgAr; 
     private PictureBox[] picBoxAr; 
     private Random r; 
     private Thread[] tArray; 
     private ThreadStart tStart; 
     private delegate void setTheImages(); 

     public Form1() 
     { 
      InitializeComponent(); 
      setImageArray(); 
      setPicBoxAr(); 
     } 

     private void setImageArray() 
     { 
      imgAr = new Image[6]; 
      imgAr[0] = testDice.Properties.Resources.die6; 
      imgAr[1] = testDice.Properties.Resources.die1; 
      imgAr[2] = testDice.Properties.Resources.die2; 
      imgAr[3] = testDice.Properties.Resources.die3; 
      imgAr[4] = testDice.Properties.Resources.die4; 
      imgAr[5] = testDice.Properties.Resources.die5; 

     } 

     private void setPicBoxAr() 
     { 
      picBoxAr = new PictureBox[6]; 
      picBoxAr[0] = pictureBox1; 
      picBoxAr[1] = pictureBox2; 
      picBoxAr[2] = pictureBox3; 
      picBoxAr[3] = pictureBox4; 
      picBoxAr[4] = pictureBox5; 
      picBoxAr[5] = pictureBox6; 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      roll(); 

      //wait for threads to finish and update images--doesn't work 
      for (int n = 0; n < 6; n++) 
      { 
       while (tArray[n].IsAlive) 
       { 
        for (int i = 0; i < 6; i++) 
        { 
         this.picBoxAr[i].Update(); 
        } 
       } 
      } 

      MessageBox.Show("Each die has its own thread"); 
     } 

     private void roll() 
     { 
      this.tStart = new ThreadStart(RunAllDiceThreads); 
      this.tArray = new Thread[6]; 
      for (int i = 0; i < 6; i++) 
      { 
       this.tArray[i] = new Thread(tStart); 
       this.tArray[i].Start(); 
      } 
     } 

     private void RunAllDiceThreads() 
     { 
      int n = 0; 
      while (n < 50) 
      { 
       setImg(); 
       Thread.Sleep(50); 
       n++; 
      } 

      for (int i = 0; i < 6; i++) 
      { 
       if (tArray[i] != null) 
       { 
        tArray[i].Abort(); 
        tArray[i] = null; 
       } 
      } 
     }// end RunAllDiceThreads 

     private void setImg() 
     { 
      r = new Random(); 

      for (int i = 0; i < 6; i++) 
      { 
        if (this.picBoxAr[i].InvokeRequired) 
        { 
         setTheImages s = new setTheImages(setImg); 
         // parameter mismatch error here 
         //this.Invoke(s, new object[] { imgAr[r.Next(6)] }); 
         //Freezes here!! 
          this.Invoke(s); 
        } 
        else 
        { 
         this.picBoxAr[i].Image = imgAr[r.Next(6)]; 
        } 
      } 
     }//end setImg 

    }// end class Form1 
}//end namespace testDice 
+0

1. Tüm program büyük bir iş parçacığı güvenlik ihlali gibi görünüyor. 2. Bunun için hiç iş parçacığına ihtiyacınız olmadığını düşünüyorum. Bunun yerine, bir zamanlayıcı kullanın ve işaretlendiğinde tüm zarları güncelleyin. – svick

+0

Ödev, thread – Mox

+0

kullanmaktır. TPL'yi kullanmayı düşünmelisiniz. – davenewza

cevap

2

Sesler programım var.

Programınızı yeniden düşünmenizi öneririm. Programınız neredeyse bireysel bir iş parçacığı ile bireysel bir kalıp modelleme kavramı üzerine inşa edilmiş gibi görünüyor. İpin halinden kalıbın durumunu kırın. Örneğin, IsRolling veya CurrentValue gibi belirli bir duruma sahip bir Die sınıfı oluşturmak isteyebilirsiniz. Çalışan iş parçanızdaki döngülerin içindeki sınıfın (ve yalnızca bu sınıfın) nesnelerini kullanın ve değiştirin. Bu şekilde, güncellemek için kullanıcı arayüzünüze geri çağırmanız gerekmeyecektir. Bağımlılıklar bu şekilde çok daha temiz. Arayüzünüzde periyodik olarak yanan (saniyede 10-30 kez), her zarın durumunu okur ve görüntüleri bu şekilde güncelleyen bir Timer oluşturmak isteyebilirsiniz. Bu, kilitlenme açısından çok daha güvenlidir, çünkü çevrimsel bağımlılığınız yoktur. Aynı zamanda daha çekici bir arayüz üretecektir, çünkü kalıp resimleriniz daha pürüzsüz, daha öngörülebilir bir şekilde güncellenecektir.

Başlıca bir kural ... Don't call Thread.Abort() (referanslara bakın). Genellikle bir Die nesnesinin bir özelliğini kullanmak için çok daha güvenlidir ve kullanıcı arayüzünüzü güncellemek için bundan okuyabilirsiniz.

+0

Yorumlar için teşekkürler. Başka bir versiyonda bir zar sınıfı yaratmaya çalışıyorum ama çok iyi gitmiyor. Zar sınıfı nesnesi Resim Kutusunu nasıl günceller? Ama evet, o yoldan gitmeye çalışıyorum. – Mox

+0

Ah, ama orada yakaladığınız var ... Eğer Dice UI iş parçacığı güncellemek zorunda yol aşağı gidiyor ısrar ediyorsanız, şimdi olduğu gibi, karmaşıklık ve kilitlenme kadar kendinizi açın. Gönderdiğiniz program, aslında bu mimariyi takip etmemek için iyi ve basit bir örnek teşkil etmektedir.Bir çok programcı bu tuzağa düşüyor - bir iş parçacığı tarafından bir nesnenin yaptığı her şeyi kapsülleme hedefini takip ederek, UI'nin işlevselliğini "model" in davranışından ayırmak için ne yapılması gerektiğinde, bu durumda, Senin zarın. http://en.wikipedia.org/wiki/Separation_of_concerns –

+0

Ne dediğinizi duyuyorum. MVC hakkında okudum ve bunu akılda tutmaya çalışıyorum. Ödevin 2 yönünü nasıl ayıracağımdan emin değilim. Her zarı kendi iş parçacığı üzerinde sarmam gerek. Her iş parçacığının Resim Kutusunun görüntüsünü güncellemesi gerekir. İş parçacığı bittiğinde, bir yöntem (ileti kutusu) çalıştırmam gerekiyor ... – Mox

0

MessageBox.Show("Each die has its own thread"); ürününü button1_Click kaldırmanız gerekir.

Kaç iş parçacığının döndüğünü takip etmek için bir özellik oluşturun. Ne zaman 6 hit MessageBox.Show("Each die has its own thread"); çağırmak (muhtemelen bu yöntemi kendi yöntemine koymak ve bu yöntemi çağırmak isteyeceksiniz).

Sorununuz, iş parçacığı başlattığınız, daha sonra ileti kutusunun gösterildiği sırada çalıştığı sırada iş parçacıklarının dönmesini beklemenizdir.

-1

.Net Framework'ün en son sürümüyle çalışabiliyorsanız, System.Threading.Tasks ad alanını kullanmanızı öneririz. Güzel olan şey, çok iş parçacıklı ayrıntıların çoğunu kaplaması ve işleri daha temiz hale getirmesidir. İşte basit bir örnek.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace TasksExample 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // holds all the tasks you're trying to run 
      List<Task> waitingTasks = new List<Task>(); 

      // a simple object to lock on 
      object padlock = new object(); 

      // simple shared value that each task can access 
      int sharedValue = 1; 

      // add each new task to the list above. The best way to create a task is to use the Task.Factory.StartNew() method. 
      // you can also use Task.Factory<RETURNVALUE>.StartNew() method to return a value from the task 
      waitingTasks.Add(Task.Factory.StartNew(() => 
      { 
       // this makes sure that we don't enter a race condition when trying to access the 
       // shared value 
       lock (padlock) 
       { 
        // note how we don't need to explicitly pass the sharedValue to the task, it's automatically available 
        Console.WriteLine("I am thread 1 and the shared value is {0}.", sharedValue++); 
       } 
      })); 

      waitingTasks.Add(Task.Factory.StartNew(() => 
      { 
       lock (padlock) 
       { 
        Console.WriteLine("I am thread 2 and the shared value is {0}.", sharedValue++); 
       } 
      })); 

      waitingTasks.Add(Task.Factory.StartNew(() => 
      { 
       lock (padlock) 
       { 
        Console.WriteLine("I am thread 3 and the shared value is {0}.", sharedValue++); 
       } 
      })); 

      waitingTasks.Add(Task.Factory.StartNew(() => 
      { 
       lock (padlock) 
       { 
        Console.WriteLine("I am thread 4 and the shared value is {0}.", sharedValue++); 
       } 
      })); 

      waitingTasks.Add(Task.Factory.StartNew(() => 
      { 
       lock (padlock) 
       { 
        Console.WriteLine("I am thread 5 and the shared value is {0}.", sharedValue++); 
       } 
      })); 

      waitingTasks.Add(Task.Factory.StartNew(() => 
      { 
       lock (padlock) 
       { 
        Console.WriteLine("I am thread 6 and the shared value is {0}.", sharedValue++); 
       } 
      })); 


      // once you've spun up all the tasks, pass an array of the tasks to Task.WaitAll, and it will 
      // block until all tasks are complete 
      Task.WaitAll(waitingTasks.ToArray()); 

      Console.WriteLine("Hit any key to continue..."); 
      Console.ReadKey(true); 
     } 
    } 
} 

Umarım bu yardımcı olur ve daha fazla yardıma ihtiyacınız olursa bana bildirin.

+0

Bu soruyla ilgili yayınladığınız kod tam olarak nasıl? – svick

+0

Amacım, kullanımı kolay olan yeni Görevler ad alanının lehine eski iş parçacığı kodunun kullanılmasından kaçınmaktır. OP, zar değerini güncellemek için iş parçacığı kullanmayı düşünürse, bunu yapmak için daha temiz bir yoldur. Ayrıca, paylaşılan kaynaklara erişirken kilitlenmeyi önlemek için kilit anahtar kelimenin kullanımını vurgular. –