비동기 호출의 병렬 처리 하기 입니다.

 

아래의 예제는 병렬 처리 하기 전입니다.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace TaskSample
{
    class Program
    {

        static void Main(string[] args)
        {
            int result3 = Method3();
            int result5 = Method5();

            Console.WriteLine(result3 + result5);
        }

        private static int Method3()
        {
            Thread.Sleep(3000); // 3초가 걸리는 작업을 대신해서 sleep 처리
            return 3;
        }

        private static int Method5()
        {
            Thread.Sleep(5000); // 5초가 걸리는 작업을 대신해서 sleep 처리
            return 5;
        }
    }
}

 

병렬로 처리 했을 경우 8초 걸릴 작업을 5초로 줄일 수 있습니다.

아래의 예제는 병렬 처리 방식이며 Task<TResult> 타입으로 구현한 예제 입니다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskSample
{
    class Program
    {

        static void Main(string[] args)
        {
            // Task를 이용해 병렬로 처리하기
            var task3 = Method3Async();
            var task5 = Method5Async();

            // task3 작업과 task5 작업이 완료될 때까지 현재 스레드를 대기
            Task.WaitAll(task3, task5);

            Console.WriteLine(task3.Result + task5.Result);
        }

        private static Task<int> Method3Async()
        { 
            return Task.Factory.StartNew(() =>
            { 
                Thread.Sleep(3000);

                return 3;
            });
        }

        private static Task<int> Method5Async()
        {
            return Task.Factory.StartNew(() =>
            {
                Thread.Sleep(5000);

                return 5;
            });
        }
    }
}

위의 예제는 모든 작업이 완료 될때까지 대기를 해야합니다.

 

Task<TResult>와 await을 사용하여 동시에 비동기 호출로 처리할 수 있습니다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // await을 이용하여 병렬로 비동기 호출: 5초 소요됨.
            DoAsyncTask();
            
            Console.ReadLine();
        }

        private static async Task DoAsyncTask()
        {
            var task3 = Method3Async();
            var task5 = Method5Async();

            await Task.WhenAll(task3, task5);

            Console.WriteLine(task3.Result + task5.Result);
        }

        private static Task<int> Method3Async()
        { 
            return Task.Factory.StartNew(() =>
            { 
                Thread.Sleep(3000);

                return 3;
            });
        }

        private static Task<int> Method5Async()
        {
            return Task.Factory.StartNew(() =>
            {
                Thread.Sleep(5000);

                return 5;
            });
        }
    }
}

ReadAllText 메서드를 비동기로 처리하기 입니다.

 

별도의 스레드를 이용하거나 델리게이트의 BeginInvoke로 처리하여 비동기를 적용하는 예제입니다.(복잡함)

using System;
using System.IO;

namespace TaskSample
{
    class Program
    {
        public delegate string ReadAllTextDelegate(string path);

        static void Main(string[] args)
        {
            string filePath = @"C:\windows\system32\drivers\etc\HOSTS";

            ReadAllTextDelegate func = File.ReadAllText;
            func.BeginInvoke(filePath, actionCompleted, func);

            Console.ReadLine();
        }

        static void actionCompleted(IAsyncResult asyncResult)
        {
            ReadAllTextDelegate func = asyncResult.AsyncState as ReadAllTextDelegate;
            string fileText = func.EndInvoke(asyncResult);

            Console.WriteLine(fileText);
        }
    }
}


위의 예제를 Task<TResult>로 바꾸면 await을 이용하여 쉽게 비동기 호출을 적용할 수 있습니다.

 

Async 메서드로 제공되지 않은 모든 동기 방식의 메서드를 비동기로 변환 가능합니다.

using System;
using System.IO;
using System.Threading.Tasks;

namespace TaskSample
{
    class Program
    {
        public delegate string ReadAllTextDelegate(string path);

        static void Main(string[] args)
        {
            string filePath = @"C:\windows\system32\drivers\etc\HOSTS";

            AwaitFileRead(filePath);

            Console.ReadLine();
        }

        static Task<string> ReadAllTextAsync(string filePath)
        {
            return Task.Factory.StartNew(() =>
            {
                return File.ReadAllText(filePath);
            });
        }

        private static async Task AwaitFileRead(string filePath)
        {
            string fileText = await ReadAllTextAsync(filePath);

            Console.WriteLine(fileText);
        }
    }
}

 

await 없이 Task타입을 단독으로 사용하는 예제 입니다.

 

Task 타입은 반환값이 없는 경우 사용되며,

Task<TResult> 타입은 TResult 형식 매개 변수로 지정된 반환값이 있는 경우로 구분됩니다.

 

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskSample
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem((obj) =>
            {
                Console.WriteLine("process workItem");
            }, null);

            Task task1 = new Task(() =>
            {
                Console.WriteLine("Process taskItem");
            });

            task1.Start();

            Task task2 = new Task((obj) =>
            {
                Console.WriteLine("process taskItem(obj)");
            }, null);

            task2.Start();

            Console.ReadLine();
        }
    }
}

 

using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FileCopy
{
    /// <summary>
    /// 메인폼 클래스 입니다.
    /// </summary>
    public partial class MainForm : Form
    {
        // Constructor (Public)

        #region MainForm() - 생성자 입니다.

        /// <summary>
        /// 생성자 입니다.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            #region 이벤트를 설정합니다.

            this.sourceButton.Click += sourceButton_Click;
            this.targetButton.Click += targetButton_Click;
            this.asyncButton.Click  += asyncButton_Click;
            this.syncButton.Click   += syncButton_Click;
            this.cancelButton.Click += cancelButton_Click;

            #endregion
        }

        #endregion

        // Event Method (Private)

        #region sourceButton_Click(sender, e) - 버튼 클릭시 동작합니다.

        /// <summary>
        /// 버튼 클릭시 동작합니다.
        /// </summary>
        /// <param name="sender">이벤트 발생자 입니다.</param>
        /// <param name="e">이벤트 인자 입니다.</param>
        private void sourceButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            if(openFileDialog.ShowDialog() == DialogResult.OK)
            { 
                this.sourceTextBox.Text = openFileDialog.FileName;
            }
        }

        #endregion
        #region targetButton_Click(sender, e) - 버튼 클릭시 동작합니다.

        /// <summary>
        /// 버튼 클릭시 동작합니다.
        /// </summary>
        /// <param name="sender">이벤트 발생자 입니다.</param>
        /// <param name="e">이벤트 인자 입니다.</param>
        private void targetButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.targetTextBox.Text = saveFileDialog.FileName;
            }
        }

        #endregion
        #region asyncButton_Click(sender, e) - 버튼 클릭시 동작합니다.

        /// <summary>
        /// 버튼 클릭시 동작합니다.
        /// </summary>
        /// <param name="sender">이벤트 발생자 입니다.</param>
        /// <param name="e">이벤트 인자 입니다.</param>
        private async void asyncButton_Click(object sender, EventArgs e)
        {
            long totalCopied = await CopyAsync(this.sourceTextBox.Text, this.targetTextBox.Text);
        }

        #endregion
        #region syncButton_Click(sender, e) - 버튼 클릭시 동작합니다.

        /// <summary>
        /// 버튼 클릭시 동작합니다.
        /// </summary>
        /// <param name="sender">이벤트 발생자 입니다.</param>
        /// <param name="e">이벤트 인자 입니다.</param>
        private void syncButton_Click(object sender, EventArgs e)
        {
            long totalCopied = CopySync(this.sourceTextBox.Text, this.targetTextBox.Text);
        }

        #endregion
        #region cancelButton_Click(sender, e) - 버튼 클릭시 동작합니다.

        /// <summary>
        /// 버튼 클릭시 동작합니다.
        /// </summary>
        /// <param name="sender">이벤트 발생자 입니다.</param>
        /// <param name="e">이벤트 인자 입니다.</param>
        private void cancelButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show("UI 반응 테스트 성공");
        }

        #endregion

        // Method (Private)

        #region CopyAsync(fromPath, toPath) - 비동기 방식으로 파일을 복사합니다.

        /// <summary>
        /// 비동기 방식으로 파일을 복사합니다.
        /// </summary>
        /// <param name="fromPath">선택 파일 경로 입니다.</param>
        /// <param name="toPath">복사할 파일 경로 입니다.</param>
        /// <returns></returns>
        private async Task<long> CopyAsync(string fromPath, string toPath)
        { 
            this.syncButton.Enabled = false;
            long totalCopied = 0;

            using(FileStream fromStream = new FileStream(fromPath, FileMode.Open))
            {
                using(FileStream toStream = new FileStream(toPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int nRead = 0;

                    while((nRead = await fromStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    { 
                        await toStream.WriteAsync(buffer, 0, nRead);
                        totalCopied += nRead;

                        this.copyProgressBar.Value = (int)(((double)totalCopied / (double)fromStream.Length) * this.copyProgressBar.Maximum);
                    }
                }
            }

            this.syncButton.Enabled = true;

            return totalCopied;
        }

        #endregion

        #region CopySync(fromPath, toPath) - 동기 방식으로 파일을 복사합니다.

        /// <summary>
        /// 동기 방식으로 파일을 복사합니다.
        /// </summary>
        /// <param name="fromPath">선택 파일 경로 입니다.</param>
        /// <param name="toPath">복사할 파일 경로 입니다.</param>
        /// <returns></returns>
        private long CopySync(string fromPath, string toPath)
        { 
            this.asyncButton.Enabled = false;
            long totalCopied = 0;

            using (FileStream fromStream = new FileStream(fromPath, FileMode.Open))
            {
                using (FileStream toStream = new FileStream(toPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024 * 1024];
                    int nRead = 0;

                    while ((nRead = fromStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        toStream.Write(buffer, 0, nRead);
                        totalCopied += nRead;

                        this.copyProgressBar.Value = (int)(((double)totalCopied / (double)fromStream.Length) * this.copyProgressBar.Maximum);
                    }
                }
            }

            this.asyncButton.Enabled = true;

            return totalCopied;
        }

        #endregion
    }
}

 

using System;
using System.IO;
using System.Threading.Tasks;

namespace ThreadNTask
{
    class Program
    {
        static void Main(string[] args)
        {
           if(args.Length < 2)
           {
                Console.WriteLine("Usage: AsyncFileIO <Source> <Destination>");
                return;
           }

           DoCopy(args[0], args[1]);

           Console.ReadLine();
        }

        static async Task<long> CopyAsync(string fromPath, string toPath)
        {
            using(var fromStream = new FileStream(fromPath, FileMode.Open))
            {
                long totalCopied = 0;

                using(var toStream = new FileStream(toPath, FileMode.Create))
                {
                    byte[] buffer = new byte[1024];
                    int nRead = 0;
                    while((nRead = await fromStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                    {
                        await toStream.WriteAsync(buffer, 0, nRead);
                        totalCopied += nRead;
                    }
                }

                return totalCopied;
            }
        }

        static async void DoCopy(string fromPath, string toPath)
        {
            long totalCopied = await CopyAsync(fromPath, toPath);
            Console.WriteLine($"Copied total {totalCopied} Bytes");
        }
    }
}
using System;
using System.Threading.Tasks;

namespace ThreadNTask
{
    class Program
    {
        static void Main(string[] args)
        {
            Caller();

            Console.ReadLine();
        }

        async static private void MyMethodAsync(int count)
        {
            Console.WriteLine("C");
            Console.WriteLine("D");

            await Task.Run(async() =>
            {
                for(int i = 1; i < count; i++)
                {
                    Console.WriteLine($"{i}/{count}.");
                    await Task.Delay(1000);
                }
            });

            Console.WriteLine("G");
            Console.WriteLine("H");
        }

        static void Caller()
        {
            Console.WriteLine("A");
            Console.WriteLine("B");

            MyMethodAsync(3);

            Console.WriteLine("E");
            Console.WriteLine("F");


        }
    }
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ThreadNTask
{
    class Program
    {
        static void Main(string[] args)
        {
            long from = Convert.ToInt64(args[0]);
            long to   = Convert.ToInt64(args[1]);

            Console.WriteLine("Please press enter to start");
            Console.ReadLine();
            Console.WriteLine("Started");

            DateTime startTime = DateTime.Now;
            List<long> total = new List<long>();

            Parallel.For(from, to, (long i) => 
            {
                if(IsPrime(i))
                { 
                    total.Add(i);
                }
            });

            DateTime endTime = DateTime.Now;

            TimeSpan ellapsed = endTime - startTime;

            Console.WriteLine("Prime number count between {0} and {1}: {2}", from, to, total.Count);
            Console.WriteLine("Ellapsed time : {0}", ellapsed);
            
        }

        static bool IsPrime(long number)
        { 
            if(number < 2)
            { 
                return false;
            }

            if(number % 2 == 0 && number != 2)
            { 
                return false;
            }

            for (long i = 2; i < number; i++)
            {
                if(number % i == 0)
                { 
                    return false;
                }
            }

            return true;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ThreadNTask
{
    class Program
    {
        static void Main(string[] args)
        {
            long from = Convert.ToInt64(args[0]);
            long to   = Convert.ToInt64(args[1]);

            int taskCount = Convert.ToInt32(args[2]);

            Func<object, List<long>> FindPrintFunc = (objRange) =>
            { 
                long[] range = (long[])objRange;
                List<long> found = new List<long>();

                for(long i = range[0]; i < range[1]; i++)
                {
                    if(IsPrime(i))
                    { 
                        found.Add(i);
                    }
                }

                return found;
            };

            Task<List<long>>[] tasks = new Task<List<long>>[taskCount];

            long currentFrom = from;
            long currentTo   = to / tasks.Length;

            for(int i = 0; i < tasks.Length; i++)w
            {
                Console.WriteLine("Tasks[{0}] : {1} ~ {2}", i ,currentFrom, currentTo);
                tasks[i] = new Task<List<long>>(FindPrintFunc, new long[]{currentFrom, currentTo});
                currentFrom = currentTo + 1;

                if(i == tasks.Length - 2)
                {
                    currentTo = to;

                }
                else
                { 
                    currentTo = currentTo + (to / tasks.Length);
                }
            }

            Console.WriteLine("Please press enter to start");
            Console.ReadLine();
            Console.WriteLine("Started");

            DateTime startTime = DateTime.Now;

            foreach(Task<List<long>> task in tasks)
            {
                task.Start();
            }

            List<long> total = new List<long>();

            foreach (Task<List<long>> task in tasks)
            {
                task.Wait();
                total.AddRange(task.Result.ToArray());
            }

            DateTime endTime = DateTime.Now;

            TimeSpan ellapsed = endTime-  startTime;

            Console.WriteLine("Prime number between {0} and {1}", from, to, total.Count);

            Console.WriteLine("Ellapsed time: {0}", ellapsed);
        }

        static bool IsPrime(long number)
        { 
            if(number < 2)
            { 
                return false;
            }

            if(number % 2 == 0 && number != 2)
            { 
                return false;
            }

            for (long i = 2; i < number; i++)
            {
                if(number % i == 0)
                { 
                    return false;
                }
            }

            return true;
        }
    }
}
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadNTask
{
    class Program
    {
        static void Main(string[] args)
        {
            string srcFile = args[0];

            Action<object> fileCopyAction = (object state) =>
            { 
                string[] paths = (string[])state;
                File.Copy(paths[0], paths[1]);

                Console.WriteLine("TaskID: {0}, ThreadID: {1}, {2} was copied to {3}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId, paths[0], paths[1]);
            };

            Task task1 = new Task(fileCopyAction, new string[]{srcFile, srcFile + ".copy1"});
            Task task2 = Task.Run(() => 
            { 
                fileCopyAction(new string[] { srcFile, srcFile + ".copy2"});
            });

            task1.Start();

            Task task3 = new Task(fileCopyAction, new string[]{srcFile, srcFile + ".copy3"});

            task3.RunSynchronously();

            task1.Wait();
            task2.Wait();
            task3.Wait();
        }
    }
}
using System;
using System.Threading;

namespace ThreadNTask
{
    class Program
    {
        static void Main(string[] args)
        {
            Counter counter = new Counter();

            Thread increaseThread = new Thread(new ThreadStart(counter.Increase));
            Thread decreaseThread = new Thread(new ThreadStart(counter.Decrease));

            increaseThread.Start();
            decreaseThread.Start();

            increaseThread.Join();
            decreaseThread.Join();

            Console.WriteLine(counter.Count);

        }
    }

    class Counter
    { 
        const int LOOP_COUNT = 1000;

        readonly object thisLock;
        bool lockedCount = false;

        private int count;
        
        public int Count
        { 
            get{ return count; }
        }

        public Counter()
        { 
            thisLock = new object();
            count = 0;
        }

        public void Increase()
        { 
            int loopCount = LOOP_COUNT;
            while(loopCount-- > 0)
            {
                lock(thisLock)
                {
                    while(count < 0 || lockedCount == true)
                    {
                        Monitor.Wait(thisLock);
                        lockedCount = true;
                        count ++;
                        lockedCount = false;

                        Monitor.Pulse(thisLock);
                    }
                }
            }
        }

        public void Decrease()
        { 
            int loopCount = LOOP_COUNT;

            while (loopCount-- > 0)
            {
                lock (thisLock)
                {
                    while (count < 0 || lockedCount == true)
                    {
                        Monitor.Wait(thisLock);
                        lockedCount = true;
                        count++;
                        lockedCount = false;

                        Monitor.Pulse(thisLock);
                    }
                }
            }
        }
    }
}

+ Recent posts