Most popular

วันอาทิตย์ที่ 1 พฤษภาคม พ.ศ. 2554

ประยุคใช้ C# Threading แยก Process การทำงานของโปรแกรมแบบ GUI

การใช้ Thread ก็คือการแยกกระบวนการทำงานออกจากกัน จากที่โปรแกรมเคยทำงานเป็นลำดับ Thread ก็เหมือนทางลัด แยกไปอีกทาง และในที่สุดก็กลับมาบรรจบกันที่ปลายทาง หรืออาจจะเดินกันเป็นแบบเส้นขนานก็ได้ แต่ที่สำคัญคือ การไม่จำกัดการทำงานอยู่เพียงแค่ Process หลักเพียงอย่างเดียว
ผลที่ได้คือการทำงานที่รวดเร็วมากขึ้น และช่วยให้ Process หลัก มี Performance ที่ดีขึ้น

หากท่านผู้;;อ่านไม่เคยศึกษาเรื่อง Thread มาเลยขอแนะนำให้ลองค้นหาจาก Google ดูก่อน ซึ่งหลายๆเว็บก็อธิบายได้เป็นอย่างดีสำหรับหลักการทำงาน แต่กรณีที่ผู้เขียนกำลังจะเขียนนี้เป็นการใช้ Thread จริง ใช้งานจริงไม่มีตัวแสดงแทน กับการนำ Thread มาใช้กับโปรแกรมแบบที่มี User Interface ที่ทุกคนก็มักจะเขียนกัน
ผู้เขียนเองก็ประสบปัญหากับการนำเอา Thread มาใช้ไม่น้อย Thread ของ .Net นั้นค่อนข้างจะแยกกันอย่างชัดเจน หากจะใช้กับ UI ก็ต้องใช้เทคนิคอย่าง delegate เข้าช่วยด้วย
ผู้เขียนได้พบการเขียนหลายรูปแบบซึ่งสามารถใช้ได้เหมือนกัน การ Design Code บางแบบก็เขียนง่าย เข้าใจง่าย บางแบบก็ย่อเสียจน...

ฟอร์มทดสอบที่ผู้เขียนสร้างขึ้นง่ายๆ มีเพียง Button และ ListBox
โดยการทำงานคือ กดปุ่มเพื่อให้ Thread ทำงานใส่ข้อมูลเข้าไปใน ListBox นั่นเอง

แบบแรกเป็นแบบที่ผู้เขียนคิดว่าเข้าใจง่ายแล้ว

private delegate void AddListBoxItemDelegate(object item); //ประกาศ delegate
private void
AddListBoxItem(object item)  //method ที่ delegate ชี้มา
{
    if (this.listBox1.InvokeRequired)  //Control เกิดเรียก Invoke เข้ามา
   {

        //เรียกใช้ method ผ่าน delegate ซ้ำ
        this.listBox1.Invoke(new AddListBoxItemDelegate(this.AddListBoxItem), item);
   }
   else
   {  
//ในกรณีที่ไม่มีการเรียก Invoke แล้วให้ดำเนินการกับ Control ได้เลย
        this.listBox1.Items.Add(item);
       
fillList();
   }
}


void fillList() //method วนลูปให้ Listbox
{
   int i = 0;
   while (true)
   {
        i++;

        listBox1.Items.Add(i);
        if (i == 4000)
            break;
   }
}
 

private void button1_Click(object sender, EventArgs e) //event on click ปุ่ม start
{

     //ประกาศ thread instance ให้ทำงานกับ method AddListBoxItem
    Thread t = new Thread(
AddListBoxItem);
     t.Start("Test");  
//ให้ thread พร้อมส่ง paramerter เพื่อทดสอบ
}


ทดสอบรันโปรแกรมแล้วกดปุ่ม Start ได้ผลลัพถูกต้องตามภาพ

 รูปแบบที่สองที่สามารถใช้ได้เช่นกัน แต่จะมีความชัดเจนกว่าในการประกาศ และเรียกใช้ Instance
 โดยที่จะประกาศ delegate type ไว้เป็น member ของ class

  public delegate void addToList(); //ประกาศไทป์; delegate
  public addToList add;  //ประกาศ instance ของ delegate

  ต่อมาจะทำการสร้าง delegate ขึ้นมาใช้ ในที่นี้ผู้เขียนสร้างที่ Constructor ของ Form
  public Form1()
  {
     InitializeComponent();
     add = new addToList(
fillList); // delegate add เรียก method signature ในที่นี้คือ fillList
     }


ปรับปรุง method fillList เล็กน้อย

void fillList()
{
   if (listBox1.InvokeRequired)
   {
       listBox1.Invoke(add);
   }
   else
   {
       int i = 0;
       while (true)
      {
         i++;
         listBox1.Items.Add(i);
         if (i == 4000)
             break;
      }
   }
}


การเรียกใช้งาน Thread ในปุ่ม Start
ต่างกับ Code แบบแรกตรงที่ method fillList ไม่มี Parameter ส่งเข้ามา
private void button1_Click(object sender, EventArgs e)
{

     //การประกาศให้ Thread ใช้ method fillList  แต่ต้องเรียกใช้โดยผ่าน delegate add
    Thread t = new Thread(
add.Invoke);
    t.Start();
}


แบบที่สาม คือการย่อให้กระชับ Code ลงมาให้น้อยลง โดย Anonymous method แต่ก็แน่นอนว่าจะต้องเสียความยืดหยุ่นไป ต่างจากสองแบบแรกที่ได้ยกตัวอย่างไป
โดยการเข้าไปจัดการโครงสร้างของ method fillList เสียใหม่ให้เป็นแบบนี้

void fillList()
{
    Invoke((MethodInvoker)delegate {
         int i = 0;
         while (true)
        {
            i++;
            listBox1.Items.Add(i);
            if (i == 4000)
                 break;
         }
     });
}


จากนั้นก็เรียกใช้เหมือนเดิม แต่เปลี่ยนจากการที่ต้องอ้าง method ผ่าน delegate ก็ไม่ต้องอ้อมค้อม เรียกใช้กันตรงๆเลย
private void button1_Click(object sender, EventArgs e)
{
     //อ้าง method fillList ให้กับ thread ได้เลย

     Thread t = new Thread(fillList);
     t.Start();
}


แบบที่สี่ คราวนี้เราจะปลดการตรวจสอบ Cross Thread ของ .Net Framework ออกเพื่อจะได้ทำงานได้สะดวกขึ้น
ตามหลักการที่เป็นมาตรฐานใน .Net Thread ห้ามทำงานมั่วกัน หมายถึงหากมีฟอร์มหนึ่งรันขึ้นมาจะถือเป็น 1 Thread อยู่แล้ว ฉนั้นโปรแกรมแบบ GUI เมื่อรันขึ้นมาก็จะเป็น 1 Thread Control ทุกตัวก็จะถือว่าอยู่บน Thread ที่ 1 ทันที จะเห็นได้ว่าหากพยายามนำ Control ไปใช้$51;น Thread ที่ได้สร้างขึ้นมาให$17;่ จะเกิด Error เกี่ยวกับ Cross Thread จึงต้องบ่งชี้การทำงานแบบเลี่ยงๆโดยใช้ delegate เข้ามาช่วย
(ซึ่งในภาษาอื่นอย่าง Delphi ค่าเริ่มต้นสามารถเขียน Thread แบบ Cross ได้ แต่ใน .Net  ค่าเริ่มต้น การเขียนแบบ Cross จะไม่ยอมให้)
ผู้เขียนจึงดัดแปลง Code ให้เป็นแบบง่ายๆ โดยเปลี่ยน method fillList เป็น

void fillList()
{
    int i = 0;
    while (true)
    {
         i++;
         listBox1.Items.Add(i);
         if (i == 4000)
            break;
    }
}

private void button1_Click(object sender, EventArgs e)
{
    //ปลดการตรวจสอบ Cross Thread บน Form1
    Form1.CheckForIllegalCrossThreadCalls = false;
 
    Thread t = new Thread(fillList);  //ให้ Thread ทำงานกับ method fillList อย่างชิวๆ
     t.Start();
}

จะเห็นได้ว่าพอปลดการตรวจสอบ Cross Thread ออกแล้ว โปรแกรมก็จะไม่ต้องยุ่งกับ delegate อีกต่อไป
แต่ถ้าถามว่าดีหรือไม่ ผู้เขียนคิดว่าคงเหมือนกับการเดินข้ามถนนโดยใช้สะพ;านลอย หรือ ไม่ใช้สะพานลอยก็ได้ ความเป็นระเบียบและความปลอดภัย ก็ย่อมต่างกัน

แบบที่ห้า เขียนแบบโคตรย่อ ปิดการตรวจสอบ Cross Thread แล้วยังจะใช้ delegate ด้วย การเขียนแบบนี้ดีในการกระชับ Code ใช้ Anonymous method แต่ต้องระวังเกี่ยวกับความยืดหยุ่นในการทำงาน หากแน่ใจว่ามีการทำงานที่นี่ ที่เดียวจบก็ลุยเลย

private void button1_Click(object sender, EventArgs e)
{
     //ปลดการตรวจสอบ Cross Thread บน Form1
    Form1.CheckForIllegalCrossThreadCalls = false;

       //สร้าง Thread พร้อมกับยัดเยียด delegate method เข้าไปใน Constructor เลย 
       Thread t = new Thread(delegate() {
         int i = 0;
         while (true)
         {
           i++;
           listBox1.Items.Add(i);
           if (i == 4000)
                  break;
           }
       });
       t.Start();
}


คงพอเข้าใจกันแล้วว่าการสร้าง และใช้ Thread กับพวก Control นั้นเป็นอย่างไร
ผู้เขียนหวังว่าคงจะเป็นประโยชน์ต่อผู้อ่านไม่มากก็น้อย ซึ่งส่วนใหญ่ Thread จะนำไปใช้กับการเขียนโปรแกรมในรูปแบบของเกมหรือกราฟฟิค แต่ถึงกระนั้นหากผู้อ่านนำไปประยุคใช้แม้ในโปรแกรมทาง Database หรือ BI โปรแกรมของท่านก็จะมีประสิทธิภาพสูงยิ่งขึ้น



  รับวางระบบงาน ระบบ Network และพัฒนาระบบ Data warehouse และ ERP ด้วยทีมงานที่มีประสบการณ์ ติดต่อได้ที่ e-mail : arrays2003@hotmail.com

1 ความคิดเห็น: