Most popular

วันพฤหัสบดีที่ 29 กันยายน พ.ศ. 2554

การรันคำสั่งด้วย Parallel ใน .net 4


ซึ่งใน .net 4 จะมี library ที่ชื่อ System.Threading.Tasks ให้เราใช้ในการจัดการกับ Task หรือคำสั่งต่างๆ เช่นเราต้องการจัดการกับเมธอดหลายๆ
 เมธอดเพื่อให้ทำงานพร้อมกัน(Concurrency) เช่นมีเมธอด

 A,B,C เราก็ใช้
Parallel.Invoke(A,B,C);
 หรือ
 Parallel.Invoke(()=>A(),
 ()=>B(),
 ()=>C()
 );

 ตัวอย่างการใช้งาน Parallel.Invoke เริ่มแรกให้เราทำการ using System.Threading.Tasks ก่อนแล้วเขียนคำสั่งต่างๆดังนี้

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication22
{
    class Program
    {
        static void Main(string[] args)
        {
            Parallel.Invoke(
                () => A(),
                () => B(),
                () => C()
                    );
            Console.ReadLine();
        }
        static void A()
        {
            Console.WriteLine("A");
        }
        static void B()
        {
            Console.WriteLine("B");
        }
        static void C()
        {
            Console.WriteLine("C");
        }
    }
}


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

ถึงแม้ว่าจะมีการเรียกใช้เมธอดให้เริ่มต้นทำงานพร้อมกันในแบบ Concurrency ก็ตามแต่เวลาที่ใช้ในการทำงานตามคำสั่งข้างในเมธอดแต่ละเมธอดจะไม่เท่ากันซึ่งเราสามารถ
 วัดเวลาในการทำงานได้โดยใช้ Stopwatch ดังนี้

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication22
{
    class Program
    {
        static void Main(string[] args)
        {
            Parallel.Invoke(
                () => A(),
                () => B(),
                () => C()
                    );
            Console.ReadLine();
        }
        static void A()
        {
            var watch = Stopwatch.StartNew();
            Console.WriteLine("A");
            Console.WriteLine("elapsed time of A is " + watch.Elapsed.TotalSeconds);
        }
        static void B()
        {
            var watch = Stopwatch.StartNew();
            Console.WriteLine("B");
            Console.WriteLine("elapsed time of B is " + watch.Elapsed.TotalSeconds);
        }
        static void C()
        {
            var watch = Stopwatch.StartNew();
            Console.WriteLine("C");
            Console.WriteLine("elapsed time of C is " + watch.Elapsed.TotalSeconds);
        }
    }
}

ซึ่งผลลัพธ์เราจะได้ดังรูป



จากรูปที่ 1 ถ้าเราเพิ่ม Thread.Sleep เข้าไปที่เมธอด B โดยให้ทำการ sleep เป็นเวลา 4 วิ ผลลัพธ์ที่ได้เราจะเห็นเวลาที่เมธอด B ทำงานตั้งแต่เริ่มต้น
 ถึงสิ้นสุดนานกว่าเมธอดอื่นที่ไม่ได้ใช้ Thread.Sleep และจะทำงานเสร็จสิ้นเป็นเมธอดสุดท้ายด้วยดังนี้

         static void B()
         {
             var watch = Stopwatch.StartNew();
             Thread.Sleep(4000);
             Console.WriteLine("B");
             Console.WriteLine("elapsed time of B is " + watch.Elapsed.TotalSeconds);
         }

 ผลลัพธ์เราจะได้ดังรูป




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

การใช้ Task
  ก่อนหน้านี้เราได้ใช้ Parallel.Invokeในการเรียกเมธอดทำงานพร้อมๆกัน ซึ่งในส่วนนี้จะมีอีกวิธีที่สามารถสั่งให้โปรแกรมทำงานพร้อมๆกันด้วยคลาสที่ชื่อ Task
 ซึ่งในการใช้ Task นั้นไม่ยุ่งยากโดยถ้าเรามีหลายๆเมธอดแล้วต้องการให้เมธอดเหล่านี้เริ่มทำงานพร้อมๆกัน เราสามารถแบ่งการทำงานของเมธอดออกเป็น Task แต่ละ Task
 ไปได้ เช่นมี 3 เมธอดคือ A,B,C ถ้าต้องการแบ่งเป็น Task เราก็เขียนได้ดังนี้

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication22
{
    class Program
    {
        static void Main(string[] args)
        {
            var t1 = new Task(()=>A());
            var t2 = new Task(() => B());
            var t3 = new Task(() => C());
            t1.Start();
            t2.Start();
            t3.Start();
            Task.WaitAll(t1, t2, t3);
            Console.WriteLine("Finished all Tasks");
            Console.ReadLine();
           
        }
        static void A()
        {
            var watch = Stopwatch.StartNew();
            Console.WriteLine("A");
            Console.WriteLine("elapsed time of A is " + watch.Elapsed.TotalSeconds);
        }
        static void B()
        {
            var watch = Stopwatch.StartNew();
            Console.WriteLine("B");
            Console.WriteLine("elapsed time of B is " + watch.Elapsed.TotalSeconds);
        }
        static void C()
        {
            var watch = Stopwatch.StartNew();
            Console.WriteLine("C");
            Console.WriteLine("elapsed time of C is " + watch.Elapsed.TotalSeconds);
        }
    }
}

ในตัวอย่างนี้เริ่มแรกให้เราสร้าง Task ขึ้นมาก่อนโดยภายใน Task ก็จะมีการกำหนดเมธอดที่ต้องการใช้งาน Task จากนั้นเรียกคำสั่ง Start เพื่อทำการสั่งให้เริ่มทำงาน Task
 จากนั้นจะมีคำสั่ง WaitAll เพื่อรอให้ทุก Task ทำงานเสร็จก่อนแล้วถึงจะทำการแสดงค่า Finished  all Tasks
 ซึ่งตรง WaitAll นี้เราสามารถกำหนดเวลาเพื่อให้โปรแกรมทำการรอได้เช่นถ้าการทำงานของ Task ทุก Task เกินเวลาที่กำหนดไว้ก็อาจจะให้แจ้งเตือนขึ้นมาว่า
 Task ที่ทำงานนั้นใช้เวลาเกินกว่าทีกำหนด ซึ่งให้เขียนดังนี้
        static void Main(string[] args)
        {
            var t1 = new Task(()=>A());
            var t2 = new Task(() => B());
            var t3 = new Task(() => C());
            t1.Start();
            t2.Start();
            t3.Start();
            if (!Task.WaitAll(new Task[] { t1, t2, t3 }, 4000))
            {
                Console.WriteLine("elapsed time > 4000 ms");
            }
            Console.WriteLine("Finished all Tasks");
            Console.ReadLine();
           
        }

ซึ่งในตัวอย่างนี้ถ้าเรารันโปรแกรมเราจะยังไม่เป็นคำสั่งในส่วนของ Console.WriteLine("elapsed time > 4000 ms");
 เนื่องจากว่าทุก Task ใช้เวลาน้อยกว่า 4000 ms  แต่ถ้าเราเพิ่ม Thread.Sleep ในเมธอดใดเมธอดหนึ่งให้ทำการ Sleep เป็นเวลา 5000 ms เช่นเพิ่ม
 ตรงเมธอด A ดังนี้
 
        static void A()
        {
            var watch = Stopwatch.StartNew();
            Thread.Sleep(5000);
            Console.WriteLine("A");
            Console.WriteLine("elapsed time of A is " + watch.Elapsed.TotalSeconds);
        }

ผลลัพธ์ที่ได้เราจะได้ดังรูป






ที่มา : คุณ paedotnet http://www.codetoday.net/default.aspx?g=posts&t=2945

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

  1. ไม่ระบุชื่อ4 ตุลาคม 2554 เวลา 17:03

    อยากทราบว่าถ้าเป็น .net 3.5 จะเขียนแนวนี้ได้มั้ยครับ

    ตอบลบ
  2. อันนี้ยังไม่เคยลองแต่คิดว่าไม่ได้ครับ
    เท่าที่ดู 4.0 จะมีอะไรใหม่ๆ มาแก้ไขความยุ่งยากของ 3.5 ครับ
    ของ 3.5 ต้องใช้ Thread กับ delegate ค่อนข้างละเอียดและยาว

    ตอบลบ