Multi-thread #1 : Once upon a time

    ตามคำขออีกแล้วครับ มีคนสนใจเรื่อง Multi-thread ผมก็เลยเลือกเอาเรื่องนี้มาเขียนก่อน ก็ Web นี้ห่างหายเรื่อง .NET Framework มานานแล้ว และก็เช่นเคยครับ Web Page แรก ผมเริ่มที่ประวัติของมันก่อน เป็นประสบการณ์ส่วนตัว ที่ผมเริ่มเรียนรู้ Multi-thread  เรามาดูกันว่า มันมีพัฒนาการอย่างไรบ้าง

    เรื่อง Multi-thread จริงๆ แล้วเป็นเรื่องที่เข้าใจไม่ยากนัก แต่ถ้าใครเคยลองเขียนดูจะรู้ว่า ถ้าคุณรู้ว่า มันสามารถสร้าง bug ได้มากมายอย่างที่คุณนึกไม่ถึงทีเดียว ผมจะค่อยๆ ชี้ให้คุณดู คุณได้จะเลี่ยงได้ถูก

 

Single Process

    OS ในสมัยก่อนนั้น รองรับการรันโปรแกรมได้เพียงหนึ่งโปรแกรมเท่านั้น เช่น DOS เป็นต้น แต่ก็มีคนหัวใสคิดค้นโปรแกรมประเภท Tenimated and Stay Resident (TSR) ซึ่งฝังตัวอยู่ในหน่วยความจำ ทำให้ดูราวกับว่า เราสามารถเรียกใช้หลายโปรแกรมได้ในเวลาเดียวกัน แต่แท้ที่จริงแล้ว เมื่อโปรแกรมประเภท TSR นั้น Pop up ขึ้นมา โปรแกรมที่ทำงานอยู่จะต้องหยุดทำงานชั่วคราว จนกว่าโปรแกรม TSR จะกลับเข้าไปฝังตัวเหมือนเดิม ถ้าใครเล่นคอมพิวเตอร์มาไม่น้อยกว่า 5 ปี น่าจะรู้จักโปรแกรม Sidekick ซึ่งถือว่าเป็นโปรแกรม TSR ที่ประสบความสำเร็จสูงสุด ผมเองก็เคยใช้ Sidekick ในการเขียนโปรแกรมเหมือนกัน

      ต่อมาก็มีโปรแกรมประเภทสลับงาน ซึ่งยอมให้ผมเปิดโปรแกรมหลายโปรแกรมพร้อมๆ กัน ถ้าผมอยากเลือกตัวไหนมาทำงานก็ใช้วิธีสลับเอา แต่มันก็ได้ที่ละงานอยู่ดี

Multitasking : Pioneer

    เมื่อประมาณ 10 ปีที่แล้ว ผมเคยใช้เครื่องคอมพิวเตอร์ของผมเป็น BBS หรือโปรแกรมที่ทำให้เครื่องของผม เป็น Server เพื่อให้คนอื่นติดต่อเข้ามาใช้บริการ แต่ปัญหามีนะครับ ถ้าผมรันโปรแกรม BBS อยู่ ผมก็ไม่สามารถทำงานอื่นได้ ตอนนั้นกำลังเห่อ C++ อยู่ ถ้าไม่ได้หัดเขียนโปรแกรมเดี๋ยวลงแดง

    ผมเลยไปเอาโปรแกรมที่ชื่อว่า Desqview มาลง โปรแกรมตัวนี้ทำให้ผมทึ่งมากครับ มันเหมือนกับโปรแกรมสลับงาน แต่โปรแกรมที่อยู่ background นั้นยังคงทำงานอยู่ ถึงแม้ว่าจะช้าลงไปมาก แต่ก็เพียงพอที่ให้ผมสามารถเปิด BBS และหัดเขียนโปรแกรมไปพร้อมๆ กัน

 

Multitasking : Graphic

    โปรแกรม Desqview ไม่ค่อยมีความ Stable เท่าที่ควร มัน Crash บ่อย จนมาถึง OS ตัวใหม่คือ Windows ผมก็เคยเล่นรุ่น 1 และ 2 มาบ้าง ความรู้สึกในขนาดนั้น ก็รู้สึกว่า Microsoft บ้ารึเปล่า ทำ OS ที่เป็น Graphic ขึ้นมา ไม่เห็นได้เรื่องอะไรเลย แต่พอมาถึง Windows 3.1 ผมก็เปลี่ยนความคิดครับว่ามันพอใช้ได้ แต่ผมไม่ได้ยุ่งกับ Windows เท่าไรนั้น จนมาถึงรุ่น 98 ก็เพราะผมใช้เวลาทั้งหมดไปกับ Linux ซึ่งเดี๋ยวเราค่อยมาคุยกัน

    Windows 3.1 ตามความรู้สึกตอนนั้น มันทำงานได้ครับ หลายงานพร้อมกันด้วย แต่ผมรู้สึกเสียดายกำลังของเครื่อง ผมว่ากว่าครึ่งมันเสียไปให้กับการทำ Graphic คนเขียนเกมส์ก็คงคิดเหมือนผม คือยังยึดติดอยู่กับ DOS

    เอาทางทฤษฎีหน่อยก็แล้วกัน การที่ Windows 3.1 สามารถทำงานหลายงานได้พร้อมกันนัน ตัว Kernel ไม่ได้มีความเก่งกาจในการสลับงานเลย ตรงนี้เป็นภาระของโปรแกรมแต่ละตัวเอง คือตัวคอมไพล์เลอร์จะไปแทรกรหัสให้กลับสู่การควบคุมของ Kernel แทรกตลอดทั้งโปรแกรม ทำให้ Kernel สามารถควบคุมโปรแกรมต่างๆ ได้ แต่ถ้า Windows เจอโปรแกรมตัวไหนแหกคอกขึ้นมาไม่แทรกรหัสดังกล่าว ก็ช่วยไม่ได้ครับ โปรแกรมที่ไม่ได้แทรกรหัสอย่างน้อย ก็พวก DOS Program นั่นแหละครับ ทางเทคนิค เราเรียกการทำ Multitasking แบบนี้ว่า Co-Operative

Multitasking : The Impression

    ยังไงผมก็ยอมรับเรื่องความช้าของ Windows ไม่ได้ โชคดีครับ มีอยู่วันหนึ่ง ผมก็ได้รับแผ่น Floopy 30 แผ่น จากลุง Bill Thompson เจ้าของ BBS หนึ่งในกรุงเทพ นัยว่าเป็น Unix สำหรับ PC รุ่นใหม่ ผมเองก็พอเคยเล่น Minix มาแล้วบ้าง แต่ก็ไม่ได้ประทับใจอะไร

    Unix ตัวนี้มีชื่อว่า Linux ครับ หลังจากที่เหนื่อยกับการ Install เสร็จแล้ว ก็ต้องเป็นทึ่งครับ เพราะมันมีเครื่องมือสำหรับการพัฒนาโปรแกรมอยู่เต็มอัตราศึกเลย  และที่สำคัญครับ ก็คือประสิทธิภาพสูงมาก สามารถรองรับ Multitask ได้หลายงาน ความเร็วของแต่ละงานสูงมากครับ เรียกว่าทึ่งสุดๆ

    ผมก็เล่นมาเรื่อยๆ ตั้งแต่รุ่นยังไม่ถึงหนึ่ง ความ stable ก็ดูดีขึ้นจากรุ่นสู่รุ่น ตั้งแต่ช่วงนั้นผมก็เล่น Linux เต็มตัว อยู่หลายปีเลยทีเดียว

    มาคุยกันทางเทคนิค อีกหน่อย คือ Linux รองรับการทำ Multitask แบบ Pre-emtive กล่าวคือมันสามารถสลับงานของ Process ใดๆ ก็ตาม โดยที่โปรแกรมเหล่านั้นไม่ต้องทำอะไรให้มันเป็นพิเศษเลย

 

พักเหนื่อยกันก่อน

    นั่นก็คือเรื่องราวพัฒนาการของ OS ต่างๆ จาก Single Task สู่ Multitask คราวนี้เราเราจะมีพัฒนาการของการเขียนโปรแกรมดูบ้าง จาก Single Thread สู่ Multi-thread เรามาลองดูกันนะครับว่า มันมีพัฒนาการอย่างไร

 

Single Thread

    อย่างที่ทราบกันว่า ในอดีตเราสามารถรันโปรแกรมได้ทีละตัว แต่ปัจจุบันมีพัฒนาขึ้นเยอะ แต่ก่อนที่เราจะทำความเข้าใจต่อไปนั้น เรามาเรียนรู้ศัพท์กันหน่อยครับ จะได้ไม่สับสน และพูดภาษาเดียวกัน

Program

    ในที่นี้ผมจะนิยามโปรแกรมว่า คือ Code ที่เป็นภาษา Assembly นั่นแหละครับ ที่อยู่ใน .exe หรือ .com มันอยู่ในสื่อที่ใช้เก็บข้อมูลต่างๆ เช่น Harddisk เป็นต้น

Process

    เรื่องนี้สับสนกันมาก แท้ที่จริงแล้ว Process คือหน่วยความจำของ Memory ที่เอาไว้ใช้ในการเก็บโปรแกรมนั่นเอง

Thread

    Thread คือตัวที่ใช้รันโปรแกรมจริงๆ ประกอบด้วย PC (Program Counter) และ Stack ครับ ตัว PC ก็คือตัวชี้บรรทัดปัจจุบันที่ทำงานนั่นเอง ส่วน Stack นั้นมีเอาไว้เก็บตำแหน่งของ PC หลังจากการ call function อื่น และยังเก็บ Local Variable, Return Value อีกด้วย

 

    โปรแกรมสมัยก่อนเช่นบน DOS เมื่อเรา Load โปรแกรมจาก Harddisk เข้าสู่ Memory มันจะกลายสภาพเป็น Process จากนั้น DOS จะนำเอา Thread มาเรียกใช้ Process นั้นครับ

 

In the Between

    เมื่อโลกของ DOS สุดสิ้นลง มายุคของ Linux, Windows ซึ่งเป็น Multitasking OS ดังนั้นมันจึงเป็นการดี ถ้าในโปรแกรมหนึ่งสามารถทำงานได้หลายงานในเวลาเดียวกัน เช่น Chat ไป download ไป เป็นต้น

    ดังนั้นแนวคิดแรกของการที่ให้โปรแกรมเดียวกันสามารถแตกตัวออกมาหลายตัวแล้วทำงานพร้อมๆ กันจึงเกิดขึ้นมา เท่าที่ผมเคยเห็น ก็จาก Unix นี่แหละครับ

    ในภาษา C ของ Unix จะมีฟังก์ชันพิเศษคือ fork() เมื่อเรียกฟังก์ชันนี้แล้ว Process ปัจจุบันจะถูก copy ออกมาอีกตัวหนึ่ง พร้อมกันนั้นมีการสร้าง Thread ใหม่เพื่อเรียกใช้ Process นั้นด้วย สิ่งนี้สร้างความแปลกใหม่ให้กับ Unix ค่อนข้างมาก โดยส่วนตัวผมเองแล้ว คำสั่ง fork() ถือได้ว่าเป็นคำสั่งที่น่าทึ่งที่สุดคำสั่งหนึ่งเท่าที่เคยพบมา

    แต่การ fork() มีจุดอ่อนครับ คือมันเปลืองที่มาก ถ้าคุณมี Internet Browser ตัวหนึ่งอาจใช้ Process ขนาด 10MB ถ้าคุณ fork() ออกมาก 10 ตัว มันก็กินหน่วยความจำไป 100 MB แล้วครับ แบบนี้ไม่ไหว

 

Multi-Thread : The Real Hero

    ถ้าผมจำไม่ผิด ผู้คิดค้นเรื่องนี้คือ Sun และ OS ตัวแรกที่มีความสามารถนี้ก็คือ Solaris แนวคิดใหม่นี้ดูดีครับ ได้มาจากการสังเกตว่า ที่เรา fork() ไปนั้น แท้ที่จริงแล้วมันก็คือ Code เดิมนั่นแหละ ทำไมไม่ลดความสิ้นเปลืองโดยให้หลาย Thread วิ่งอยู่บน Code เดียวกัน ถ้าทำได้จะทำให้ประหยัดหน่วยความจำขึ้นมาก และ Sun ก็ทำสำเร็จครับ

    จากนั้น Posix ซึ่งเป็นมาตรฐานการสร้าง Unix ก็ได้เอาแนวคิดของ Sun ไปปรับปรุงต่อให้เป็นมาตรฐาน และตั้งชื่อว่า pthread ซึ่งภาษา Java ก็เอาแนวคิดของ pthread ไปใช้จนประสบผลสำเร็จอย่างสูง โดยส่วนตัวแล้ว ผมว่าภาษา Java นี่แหละที่ทำให้ Multi-thread เป็นที่รู้จัก และ แสดงให้เห็นถึงการประยุกต์ใช้ มากกว่าการมองว่าเป็นเพียง "ของเล่น"

 

ทิ้งท้าย

    แน่นอนครับว่า C# ไม่ยอมน้อยหน้า Java เป็นอันขาด ภาษา C# มีการออกแบบการทำ Multi-thread ใหม่ทั้งหมด ทำให้ง่ายขึ้นกว่า Java มากเลยครับ เราจะได้เรียนเรื่องนี้กันในบทต่อๆ ไป

    เนื้อหาของการทำ Multi-thread มีรายละเอียดเยอะครับ ถ้าเขียนจริงๆ แล้วอาจจะได้เป็นหนังสือ 1 เล่มโดยเฉพาะเลย เพราะเนื้อหากว้างมาก ครอบคลุมการเรียกใช้โปรแกรม การคุยกันระหว่าง Thread และการควบคุม Thread รายละเอียดเยอะครับ

    ผมจะกล่าวให้ฟังในบทต่อๆ ไปครับ