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

 

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

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
    }
}

 

+ Recent posts