Tag Archives: NSThread

Objective-C Programming Chapter 14 (Part 2)

Mutual Exclusion

จากปัญหา race condition เราสามารถใช้ Mutual Exclusive Access ซึ่งเป็นการกำหนดส่วนที่ให้เทรดสามารถเข้าใช้ทรัพยากรได้ทีละเทรด เราเรียกพื้นที่ตรงส่วนนี้ว่า Critical Section และการขอเข้าใช้ทรัพยากรส่วนนี้จะเรียกว่า mutex lock การทำงานของ mutual exclusive access เหมือนเราขอลูกกุญแจเพื่อเข้าไปเปิดประตู เมื่อเราเข้าไปในห้องแล้วประตูก็จะปิดอัตโนมัติไม่ให้คนอื่นเข้าไปใช้ได้นอกจากคนที่มีกุญแจ หลังจากทำงานเสร็จเราก็ต้องคืนลูกกุญแจเพื่อให้คนอื่นได้ใช้ต่อไป การใช้ mutex lock นี้สามารถทำได้โดยการใช้การใช้คลาส NSLock เราจะแก้ไขคลาส Producer โดยเพิ่มการเพิ่ม NSLock ดังนี้

Program 14.6
Producer.h

Producer.m

Continue reading Objective-C Programming Chapter 14 (Part 2)

Objective-C Programming Chapter 14 (Part 1)

Chapter 14

Concurrent Programming

การทำงานแบบ concurrent หรือการทำงานหลายๆอย่างพร้อมๆกัน ทั้งใน Mac OS X และ iOS ได้แบ่งระดับของ API ไว้เป็นหลายระดับ ซึ่งก็มีข้อดีข้อเสียและข้อจำกัดต่างๆในแต่ละดับแตกต่างกันไป ในบททนี้เราจะได้เรียนรู้ API พื้นฐานเช่น NSThread เพื่อนำไปต่อยอดกับ Grand Central Dispatch ในบทต่อไป การทำงานแบบ concurrent จะประกอบด้วยสองส่วนสำคัญคือ process และ thread

Process โปรเซสหมายถึงโปรแกรมที่กำลังถูกประมวลผลหรือทำงานอยู่ในขณะนั้น เช่นเมื่อเปิดโปรแกรม A ระบบปฎิบัติการจะสร้างตัวแทน (instance) ของโปรแกรมขึ้นมาทำงานซึ่งนั่นก็คือ process ในการทำงานของโปรเซสอาจจะประกอบไปด้วย thread จำนวนมาก หรือทำงานเพียงหนึ่ง thread

Thread เทรดคือหน่วยการทำงานย่อยๆที่เกิดขึ้นในโปรเซส หากโปรเซสมีเพียงเทรดเดียวจะเรียกว่า single threading  และถ้าโปรเซสมีหลายเทรดก็จะเรียกว่า multithreading โดยที่เทรดแต่ละตัวจะทำงานอิสระแยกจากกัน เนื่องจากการทำงานของ CPU สามารถทำงานได้ทีละอย่าง ดังนั้นในระบบที่มีการทำงานแบบ multithreading เวลาในทำงานของ CPU จะถูกแบ่งเป็นส่วนย่อยๆ เพื่อใช้ในการประมวลผลแต่ละเทรด การจัดสรรเวลาการทำงานของเทรดจะมี schduler ของระบบปฎิบัติการเป็นคนจัดการ เมื่อเทรดทำงานจนครบเวลาที่ schduler จัดไว้ให้ ระบบจะทำการเก็บสถานะของเทรดที่ทำงานในขณะนั้น แล้วหยุดการทำงานไว้ จากนั้นจะสลับให้อีกเทรดทำงาน ซึ่งเรียกว่า context switch และเมื่อครบตามเวลาที่กำหนด เทรดก็จะถูกสลับเปลี่ยนไปแบบนี้เรื่อยๆ เนื่องจากการสลับการทำงานระหว่างเทรดน้ันเกิดขึ้นเร็วมาก ทำให้ดูคล้ายกับว่าเทรดทำงานพร้อมๆกัน ในกรณีคอมพิวเตอร์มี CPU มากกว่าหนึ่งตัว (Multicore) ระบบปฎิบัติการอาจจะแบ่งเทรดไปทำงานในแต่ละ CPU Core โดยที่แต่ละ CPU อาจจะมีหนึ่งเทรดหรือหลายเทรดทำงานอยู่ ในระบบคอมพิวเตอร์ที่ CPU ทำงานพร้อมกันหลายๆตัวจะเรียกการทำงานแบบนี้  Parallel ซึ่งหลายคนเข้าใจผิดว่าการทำงานแบบ parallel เหมือนกับ mutithread

โปรแกรมตั้งแต่บทแรกที่เราได้เขียนกันมาเป็นการทำงานแบบ Single threading หรือทำงานเพียงหนึ่งอย่าง การทำงานแบบนี้มีข้อดีคือจัดการทุกอย่าง เป็นอย่างๆไปตั้งแต่ต้นจนจบ ทำให้การบริหารจัดการง่าย แต่ก็มีข้อเสียคือ เสียเวลาไปกับรอให้การทำงานแต่ละอย่างเสร็จสิ้นหรือเรียกว่า idle เช่น สมมติว่า มีงาน 2 งานคืองาน A กับ C โดยที่งาน A นั้นให้ผู้ใช้งานกดปุ่มจึงสามารทำงานได้ ส่วนงาน C สามารถทำได้เลย ในระบบที่เป็น single threading จะเห็นว่าเราสูญเสียเวลาไปกับการรอให้ A ทำงานจนเสร็จ ถึงจะเริ่มการทำงาน C ได้ และเราเรียกเวลาที่ไม่ได้งานอะไรนี้ว่า idle ส่วนในระบบ multithread งาน C สามารถทำได้เลยโดยที่ไม่ต้องรอ A ทำงานเสร็จ เพื่อลด idle และทำให้ CPU เกิดประสิทธิภาพสูงที่สุดเราจึงต้องแบ่งการทำงานออกเป็นหลายๆเทรด

NSThread

การสร้างเทรดตามแบบเดิมนั้นจะใช้ฟังก์ชั่นภาษา C ในการสร้าง thread หรือเรียกว่า POSIX Thread (pthread) ซึ่งเป็นคำสั่งในระดับล่าง (Low Level) และในปัจจุบันเราไม่จำเป็นต้องใช้ pthread เพราะมีคลาสที่ช่วยให้การสร้างเทรดนั้นง่ายขึ้นนั่นก็คือ NSThread การสร้างเทรดด้วย NSThread มีอยู่ด้วยกันสองวิธีแบบแรกคือสร้างอ็อบเจ็กของ NSThread ขึ้นมาใช้งาน แบบที่สองคือใช้คลาสเมธอด detachNewThreadSelector:toTarget:withObject: โปรแกรมที่เราจะได้เขียนกันเป็นโปรแกรมแรกคือโปรแกรมจำลองการ download ซึ่งมีโค้ดดังนี้ Continue reading Objective-C Programming Chapter 14 (Part 1)

Ojective-C Programming – Thread II

หลังจากเขียน Thread พอเป็นแล้วก็ยังมีเรื่องเกี่ยวกับ Thread ที่ต้องรู้อีก อยู่อย่างหนึ่งก็คือ การ Synchronizes ระหว่าง Thread

อย่างที่บอกไปแล้ว ว่าการเขียนโปรแกรมโดยการใช้ Thread นั้นมีข้อดีมากมาย แต่ก็มีข้อเสียเหมือนกัน และหนึ่งในข้อเสียนั้นก็คือการเขียนโปรแกรมแบบที่มีหลายๆ thread นั้นทำให้โปรแกรมของเราซับซ้อนมากขึ้น และลำบากต่อการจัดการข้อมูลที่มีการใช้งานร่วมกันระหว่าง thread เช่นเป็นต้นว่า

ถ้าโปรแกรมที่เราเขียนขึ้นมีหลายๆ Thread โดยแต่ละ Thread นั้นมีการแชร์ข้อมูลร่วมกัน ถ้าหากเราไม่ได้เขียนโปรแกรมให้มันมีการควบคุมการทำงานของแต่ละ Thread หรือเขียนได้ไม่ดี อาจจะเกิดเหตการณ์ในลักษณะแบบนี้

สมมติว่า โปรแกรม ของเรามี 2 thread ที่มีการใช้ตัวแปรร่วมกันคือ Money และแต่ละ thread นั้นก็จะทำการอ่านค่า Money และเขียนผลลัพธ์ที่ได้จากการคำนวนใหม่ลงไป

  1. Thread A : อ่านตัวแปร Money ได้มีค่าเป็น 10$ และระหว่างการคำนวนนี้ ใช้เวลาประมาณ 10 นาโนวินาที
  2. และระหว่างที่ A กำลังคำนวนอยู่นั้น Thread B ก็ได้อ่านค่า Money เหมือนกัน ได้ค่า 10$ เหมือนกัน แต่ใช้เวลาในการคำนวนเพียงแค่ 5 นาโนวินาที
  3. เนื่องจาก ว่า B นั้นทำงานเสร็จก่อน A ถึง 5 นาโนวินาที. Thread B จึงทำการ เขียนค่า Money เข้าไปใหม่เป็น 30$
  4. เมื่อ A ทำงานเสร็จ A ก็เลยเขียนค่า Money ที่ได้จากการคำนวนใหม่เป็น 15$

**** จะเห็นว่าค่าผลลัพธ์ที่ได้นั้น มันเกิดการ เขียนทับกันขึ้นมา ลองจินตนาการว่า สมมติว่า มี คนสองคนทำการฝากเงิน ในเวลาพร้อมๆกัน เข้าบัญชีเดียวกัน ขณะที่นาย A กำลังฝากเงินต้องใช้เวลาในการประมวลเสร็จใช้เวลา 10 วินาทีและในระหว่างนี้ นาย B ก็ฝากเงินมาเหมือนกัน แต่ใช้เวลา 5 วินาที ถ้าหากเราไม่มีการจัดการตรงนี้ นั่นก็แปรว่า เมื่อข้อมูลในบัญชี update จากการฝากเงินของ B ก็จะถูกเขียนทับด้วย ข้อมูลใหม่ที่นาย A ได้ฝากเข้ามา นันก็แปลว่า ข้อมูลเงินฝากนั้นผิดพลาด สิ่งที่ควรจะเป็นก็คือ ต้องรอให้ A ทำงาน เสร็จก่อน แล้ว B จึงอ่านข้อมูล ****

ลองมาเขียนโปรแกรมสักโปรแกรม โดยโปรแกรมนี้มี 2 Thread โดยที่
thread เป็นการเพ่ิมจำนวน member ใน array, ส่วนอีก thread นั้นทำหน้าที่ดึงข้อมูลจาก array แล้วทำการเปลี่ยนแปลงค่า

ลองดูโปรแกรม ข้างล่างนี้

และจากการ Compile & Run โปรแกรม ผลลัพธ์ที่ได้จะมีเป็นประมาณนี้

0 Hello World!!
1 hello World!!
.
.
8 Hello World!!
9 Hello World!!
10 Hello World!! World!!
11 Hello World!! World!!
12 Hello World!! World!!
13 Hello World!! World!!
14 Hello World!! World!!
15 Hello World!!
16 Hello World!!
17 Hello World!!
18 Hello
.
.
98 Hello
99 Hello

จากผลลัพธ์จะเห็นว่า เนื่องจาก Thread ทั้งสองคือ thread ที่เรียกใช้งาน AddMember ที่ทำหน้าที่เพิ่มจำนวน member ใน m_array  และ อีก thread ทำหน้าที่ AddMoreText ทำหน้าที่เรียกใช้ ตัวแปร m_array และแก้ไขค่า ในแต่ละ member ของ m_array ซึ่งสอง thread ทำงานแยกจากกันและเริ่มทำงานพร้อมกัน

ดังนั้น ในขณะที่ Thread ทั้งสองทำงานและมีการใช้งานข้อมูลร่วมกัน โดยไม่มีการ synchronize ข้อมูลระหว่างกันว่า m_array ว่าตอนนี้ข้อมูลเป็นอย่างไร จึงเป็นเหตุทำให้โปรแกรมทำงานผิดพลาดได้ ( มันควรจะเป็น Hello World!! ทั้งหมด ไม่ใช่ Hello World!! World!! หรือ Hello อย่างเดียว )

วิธีการแก้ปัญหา หรือป้องกัน สิ่งที่เกิดขึ้น มักนิยมใช้ คือ การ Lock ตัวแปร หรือ code ให้ทำงานได้ทีละ Thread เพื่อกันไม่ให้ Thread อื่นๆเข้าใช้จนกว่าจะใช้งานเสร็จ

NSLock

เราจะใช้ class ที่ชื่อ NSLock เพื่อจะเข้ามาช่วยในการจัดการ การทำงานของแต่ละ thread โดยมันจะทำหน้าที่ lock code ในส่วนที่ต้องการให้สามารถเข้าทำงานได้ทีละ Thread ไว้ ส่วน thread อื่นๆ ก็ต้องทำการรอ จนกว่า จะมีการ unlock เกิดขึ้น

การใช้งาน NSLock นั้น ก็เพียงแค่ประกาศ NSLock ขึ้นมา และเรียกใช้ lock หรือ unlock เท่านั้นเอง

จากตัวอย่าง code ข้างบน ก็แก้ไข ส่วนของ interface โดยการเพิ่มตัวแปร NSLock และในส่วนของ method ก็ทำการ เรียกใช้ lock และ unlock

ส่วน Method ก็ทำการแก้ไขได้เป็น

เพียงเท่านี้ก็แก้ปัญหาได้แล้ว สำหรับผลลัพธ์ ที่่ออกมาก็คือ

0 Hello World!!
1 Hello World!!
.
.
98 Hello World!!
99 Hello World!!

จะเห็นว่า จะไม่เกิดการซ้อนทับ เหมือนกับ ตัวอย่างแรก

@synchronized

ยังมีอีกวิธี ที่สามารถทำการ lock ในส่วนที่ต้องการได้ ถ้าไม่ต้องการใช้ NSLock เราเพียงแค่เขียน code ที่ต้องการจะ lock ไว้ภายในส่วนของ @synchronized { } ก็ทำได้เหมือนกัน

เป็นต้นว่า

ก็ติดปัญหาอะไร ถามได้น่ะครับ

Ojective-C Programming – Thread

ก็ยังคงต่อเนื่องด้วย Objective-C เหมือนเดิม ว่าจะขึ้น cocoa มา 3 รอบแล้วก็ยังไม่ได้ขึ้นสักที บอกตั้งแต่ iPhone SDK เพิ่งจะออกจนตอนนี้มันเป็น Beta 6 ไปละ หวังไม่โกรธกันนะครับ เอาละวันนี้เข้าเรื่องเลยดีกว่า

Thread

คืออะไร ?  ก็อธิบายได้ง่ายๆว่า มันคือส่วนย่อยๆของ Process หรือชุดคำสั่งนั่นเหละ โดยทำงานแยกจากกัน โดยปกติแล้วโปรแกรมที่เขียนขึ้นมาง่ายๆมักจะเป็นลักษณะ 1 thread หรืออาจจะเรียกได้ว่า “Single Thread” ถ้ามีหลายๆ thread ก็เรียกว่า “Multithread” สำหรับโปรแกรมใหญ่ๆแล้วจะมีการใช้ thread มากกว่า 1

การใช้งาน thread มีข้อดีหลายอย่างตั้งแต่ การใช้ทรัพยาการร่วมกัน การสนับสนุนการทำงานของ multiprocessor และอื่นๆอีกมากมาย สำหรับใครที่ไม่เคยรู้เรื่อง thread นั้นแนะนำให้ไปอ่านเพิ่มเติม

http://www.thaiall.com/os/os04.html

http://en.wikipedia.org/wiki/Thread_(computer_science)

เมื่อพอเข้าใจเบื้องต้นแล้ว เข้าสู้เนื้อหาของเราเลยดีกว่า ว่าเราจะสร้าง Thread และใช้งานมันได้อย่างไร

NSThread

ใน objective-c นั้นสามารถทำได้ตั้งแต่การเรียกใช้ thread ของ ภาษา c เช่น pthread แต่ไหนๆเราจะเขียน objective-c กันแล้ว จะไปใช้ pthread กันทำไม เพราะใน objective-c เองนั้นก็มี class ที่อำนวยความสะดวกในการสร้าง thread อยู่แล้วนั่นคือ NSThread

การสร้าง thread นั้นทำได้ อยู่ 2 วิธีคือ

  • เรียกใช้ class method ที่มีชื่อว่า detachNewThreadSelector:toTarget:withObject:

การสร้าง thread ด้วย detachNewThreadSelector แบบนี้เราไม่จำเป็นต้องประกาศ ตัวแปรอะไรเลย จะมีก็แต่ parameter ที่ต้องใส่เข้าไป มาดูการประกาศ thread อีกแบบกันเลย

  • สร้าง NSThread ขึ้นมาแล้วเรียก start

ส่วนแบบวิธีนี้ต่างจากอันแรกคือ เราสร้างตัวแปร NSThread ขึ้นมาแล้วก็ใส่ค่า parameter ให้กับมัน หลังจากนั้นก็เรียก start สำหรับการสร้างแบบนี้มีข้อดี ต่างจากอย่างแรกก็คือ เราสร้างไว้ก่อนแล้วค่อยเรียกให้ thread start ทีหลังได้

ก็หลังจากสร้างเป็นแล้ว มาดูการใช้งาน กันเลยดีกว่า

สมมติว่า เราจะเขียนโปรแกรม ที่เอาไว้เขียน console แบบง่ายๆกัน แต่เป็นแบบ หลายๆ thread

จากโปรแกรม ข้างบน อธิบายคร่าวๆนะครับว่า เราประกาศ  class ชื่อ Console ขึ้นมาโดยที่มี method  เพียงแค่ PrintToConsole โดยการทำงานของ method นี้ก็คือให้พิมพ์ข้อความทั้งหมด 5 ครั้ง

หลังจากนั้นก็ทำการประกาศ ตัวแปร Console ขึ้นมาอีก 3 โดยแต่ละ object นั้นก็ทำการพิมพ์ข้อความออกมาไม่เหมือนกันนั่นก็คือ Hello , Sample , Ok

ลำดับต่อไปก็ทำการ สร้าง Thread ขึ้นมา โดยจากตัวอย่าง ผมได้ทำการใช้วิธีการประกาศทั้ง 2 แบบคือทั้งแบบ ประกาศ ตัวแปร NSThread ขึ้นมาก่อน แล้วค่อยเรียก Start และอีกแบบก็คือไม่ต้องประกาศ ตัวแปร แต่เรียกใช้ class method ขึ้นมาเลย

ยังมีอีก method ที่น่าสนใจก็คือ sleepForTimeInterval โดยทำหน้าที่คือ หยุดการทำงานตามเวลาที่กำหนด สาเหตุก็เพราะว่า เนื่องจากว่า Thread นั้นทำงานแยกกัน อาจจะมีบาง Thread ที่ทำงานเสร็จก่อน หรือเสร็จหลัง Main Program ก็ได้ ฉนั้นจึงทำการรอให้ แต่ละ Thread ทำงานเสร็จก่อน แล้วค่อยจบโปรแกรม เพื่อที่ว่าป้องกันปัญหา Memory Leak หรือโปรแกรมทำงานผิดพลาด

และหลังจาก compile และ run แล้วผลลัพธ์ที่ได้ จะได้ประมาณแบบนี้

จะเห็นว่า ลำดับ แต่อันจะไม่เรียงกัน อาจจะสลับกันไปมา ก็เนื่องจากว่า Thread แต่ละอันนั้นทำงานแยกจากกัน

*** ข้อระวังสำหรับการใช้ Thread ใน Objective-C ***
ใน Thread ถ้ามีการประกาศตัวแปร หรือว่าใช้งานในลักษณะที่ต้องมีการจอง memory ต้องมี NSAutoreleasePool ด้วยเสมอ

เช่น

– (void) PrintToConsole : NSString* text  ถ้าเราจะเปลี่ยน code การทำงานให้มันพิมพ์ออกที่หน้า console เหมือนกันแต่เราไม่อยากใช้ NSLog ก็อาจจะเขียนใหม่ได้ว่า

การเขียนแบบนี้เวลา run จะเกิด error น่ะครับ เพราะว่า [text UTF8String] มันจะไปจอง memory ใหม่แล้วส่งค่า char* กลับมา ฉนั้น ถ้าไม่มี NSAutoreleasePool มันจะ error

การเขียนที่ถูกต้องควรจะเป็น

สำหรับ tutorial นี้จริงๆผมกะว่าจะเป็น guide ง่ายๆสำหรับคนที่รู้เรื่อง thread อยู่แล้ว ก็ถ้าใครยังไม่เข้าใจ ต้องการรู้เรื่องเกี่ยวกับ thread เพิ่มมากขึ้นก็แนะนำให้ไปอ่าน link ที่ข้างบน แล้วก็ค้นหาจาก google ก็ได้ครับ

ครั้งหน้า ก็ยังอยู่กับ objective-c น่ะครับ ส่วน source สำหรับ วันนี้ก็โหลดได้ที่นี่เลย