Most popular

วันพฤหัสบดีที่ 26 พฤษภาคม พ.ศ. 2554

ใช้ Lock ทำงานที่ละ Thread

ส่วนมากการทำงานของโปรแกรมที่เขียนแบบ Multi-Threading จะมีจุดประสงค์ให้ทำงานพร้อมๆกันในเวลาเดียว
แต่ก็มีหลายกรณีที่จำเป็นต้องบังคับให้ Thread ทำงานตามๆกันไปโดยไม่พร้อมกัน การทำให้ Thread ทำงานจนจบก่อนที่จะไปทำที่ Thread อื่น
ผู้เขียนจะทำตัวอย่างโปรแกรมโดยใช้ คำสั่ง lock(Instance){} เพื่อเป็นการทำให้การทำงาน Thread ทำงานเสร็จก่อน โดยจะมี Control ProgressBar 4 ตัว ที่จะทำงานตามลำดับกันไป GUI ตามภาพด้านล่าง

ผู้เขียนจะไม่ทำงานตั้งค่าเริ่มต้นของแต่ละ Control .ในขั้นตอน Design ที่จะทำการตั้งค่าทั้งหมดตอน Runtime โดยที่
  • ฟอร์ม ตั้งชื่อ threadSEQ
  • ปุ่ม Start ตั้งชื่อ StartBtn
  • ProgressBar ทั้ง 4 ให้มีชื่อเป็น Default
ผู้เขียนสร้าง Type ขึ้นมา 2 กลุ่มคือ

enum setprogress { spReset,spStart}
enum setThread{stCreate,stAbort}

Member ของ Class มีอยู่ตัวเดียวคือ
private Thread[] t;
t เป็น Member ที่เป็น array ของ Thread ทั้งหมด
method ที่เป็นการกำหนดค่าต่างๆ และทำให้ Thread ต่างๆ ถูกสร้างขึ้นคือ
        //method setAllProgress มี parameter เป็น Type  setprogress  ที่สร้างขึ้นตอนแรก และมี Pass by referance นับค่า Thread ที่เกิดขึ้น
        private void setAllProgress(setprogress value,ref int ThreadPGCount)
        {
            //ให้เริ่มต้นนับ Thread เป็น 0
            ThreadPGCount = 0;
            //สร้าง Object Random เพื่อใช้ในการสุ่มค่าสูงสุดของ ProgressBar
            Random rd = new Random();

            //วนลูป Control ทั้งหมดที่อยู่บนฟอร์ม
            foreach (Control c in this.Controls)
            {
                //ถ้า Control ที่พบใช่ชนิด ProgressBar 
                if (c is ProgressBar)
                {
                    ProgressBar p = (ProgressBar)c;
                    //ตรวจสอบคำสั่งที่ให้ทำงาน
                    if (value == setprogress.spReset)
                    {
                        p.Minimum = 0;
                        p.Maximum = rd.Next(100,500);
                        p.Step = 1;
                        p.Value = 0;
                    }
                    else if (value == setprogress.spStart)
                    {
                        this.t[ThreadPGCount].Start(p);
                    }

                    //นับจำนวน Thread ที่จะใช้ทำงาน
                    ThreadPGCount++;
                }
            }
        }

method runprogress การทำงานของ Thread

        private void runprogress(object o)
        {
            //แปลงค่า Control ProgressBar ที่ส่งเข้ามา
            ProgressBar pg = (ProgressBar)o;

            lock (this)
            {
                while (pg.Value < pg.Maximum)
                {
                    pg.PerformStep();
                    Thread.Sleep(10);
                    this.Text = "Runing " + pg.Value + "(" + pg.Maximum + ")";
                    this.Update();
                }
            }
        }

        private void setAllThread(setThread value)
        {
            //วนลูป Thread ทั้งหมด
            for (int i = 0; i < this.t.Length; i++)
            {
                if (value == setThread.stCreate) //ให้สร้าง Thread
                    this.t[i] = new Thread(this.runprogress);
                else if (value == setThread.stAbort) //ให้จบการทำงานของ Thread
                    this.t[i].Abort();
            }
        }
method ต่างๆนำไปใช้งานภายใน Constructor เพื่อให้กำหนดค่าทั้งหมด
        public threadSEQ()
        {
            InitializeComponent();
            threadSEQ.CheckForIllegalCrossThreadCalls = false;

            int tid = 0;  //เก็บจำนวน Thread ที่ต้องใช้
            this.setAllProgress(setprogress.spReset,ref tid);

            this.t = new Thread[tid]; //สร้าง Thread array ตามจำนวน Thread ที่ต้องใช้
            this.setAllThread(setThread.stCreate);
        }
ปุ่ม Start เรียกใช้ method ที่ได้สร้างขึ้นมาแล้ว
        private void StartBtn_Click(object sender, EventArgs e)
        {
            int i=0;
            this.setAllProgress(setprogress.spStart,ref i);
        }
ที่จะลืมไม่ได้ก็คือตอนมีการปิดฟอร์ม ก็ต้องให้ Thread หยุดทำงานทั้งหมด เพื่อป้องกันการทำงานของ Thread ที่อาจจะค้างอยู่ได้
        private void Form2_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.setAllThread(setThread.stAbort);
        }
ภาพรวมของ Class ทั้งหมด


ทดสอบการทำงานของโปรแกรมเมื่อกดปุ่ม Start 
ProgressBar แต่ละตัวก็จะทำงานโดยรอทำงานตามกันไป ไม่ทำงานพร้อมกัน



Code ทั้งหมดของฟอร์ม threadSEQ
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace testThread
{
    public partial class threadSEQ : Form
    {
        enum setprogress { spReset,spStart}
        enum setThread{stCreate,stAbort}

        private Thread[] t;

        public threadSEQ()
        {
            InitializeComponent();
            threadSEQ.CheckForIllegalCrossThreadCalls = false;
            int tid = 0;
            this.setAllProgress(setprogress.spReset,ref tid);

            this.t = new Thread[tid];
            this.setAllThread(setThread.stCreate);
        }

        private void setAllProgress(setprogress value,ref int ThreadPGCount)
        {
            ThreadPGCount = 0;
            Random rd = new Random();
            foreach (Control c in this.Controls)
            {
                if (c is ProgressBar)
                {
                    ProgressBar p = (ProgressBar)c;
                    if (value == setprogress.spReset)
                    {
                        p.Minimum = 0;
                        p.Maximum = rd.Next(100,500);
                        p.Step = 1;
                        p.Value = 0;
                    }
                    else if (value == setprogress.spStart)
                    {
                        this.t[ThreadPGCount].Start(p);
                    }
                    ThreadPGCount++;
                }
            }
        }

        private void setAllThread(setThread value)
        {
            for (int i = 0; i < this.t.Length; i++)
            {
                if (value == setThread.stCreate)
                    this.t[i] = new Thread(this.runprogress);
                else if (value == setThread.stAbort)
                    this.t[i].Abort();
            }
        }

        private void runprogress(object o)
        {
            ProgressBar pg = (ProgressBar)o;

            lock (this)
            {
                while (pg.Value < pg.Maximum)
                {
                    pg.PerformStep();
                    Thread.Sleep(10);
                    this.Text = "Runing " + pg.Value + "(" + pg.Maximum + ")";
                    this.Update();
                }
            }
        }

        private void StartBtn_Click(object sender, EventArgs e)
        {
            int i=0;
            this.setAllProgress(setprogress.spStart,ref i);
        }

        private void Form2_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.setAllThread(setThread.stAbort);
        }
    }
}

ไม่มีความคิดเห็น:

แสดงความคิดเห็น