C#

async 및 await를 사용한 비동기 프로그래밍 - 1

소나무꼴 2019. 9. 10. 17:26

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/async/

 

C#의 비동기 프로그래밍

async, await 및 Task를 사용하여 비동기 프로그래밍을 지원하는 C# 언어에 대해 간략히 설명합니다.

docs.microsoft.com

목차 : 

1. https://pinelike.tistory.com/34?category=786274
2. https://pinelike.tistory.com/35?category=786274
3. https://pinelike.tistory.com/36?category=786274
4. https://pinelike.tistory.com/37?category=786274
5. https://pinelike.tistory.com/38?category=786274
5. https://pinelike.tistory.com/39?category=786274


 

1. async 적용전 코드

...더보기

 

private void SumPageSizes()
{
    // Make a list of web addresses.
    List<string> urlList = SetUpURLList();

    var total = 0;
    foreach (var url in urlList)
    {
        // GetURLContents returns the contents of url as a byte array.
        byte[] urlContents = GetURLContents(url);

        DisplayResults(url, urlContents);

        // Update the total.
        total += urlContents.Length;
    }

    // Display the total count for all of the web addresses.
    resultsTextBox.Text += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
}

private List<string> SetUpURLList()
{
    var urls = new List<string>
    {
        "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
        "https://msdn.microsoft.com",
        "https://msdn.microsoft.com/library/hh290136.aspx",
        "https://msdn.microsoft.com/library/ee256749.aspx",
        "https://msdn.microsoft.com/library/hh290138.aspx",
        "https://msdn.microsoft.com/library/hh290140.aspx",
        "https://msdn.microsoft.com/library/dd470362.aspx",
        "https://msdn.microsoft.com/library/aa578028.aspx",
        "https://msdn.microsoft.com/library/ms404677.aspx",
        "https://msdn.microsoft.com/library/ff730837.aspx"
    };
    return urls;
}

private byte[] GetURLContents(string url)
{
    // The downloaded resource ends up in the variable named content.
    var content = new MemoryStream();

    // Initialize an HttpWebRequest for the current URL.
    var webReq = (HttpWebRequest)WebRequest.Create(url);

    // Send the request to the Internet resource and wait for
    // the response.
    // Note: you can't use HttpWebRequest.GetResponse in a Windows Store app.
    using (WebResponse response = webReq.GetResponse())
    {
        // Get the data stream that is associated with the specified URL.
        using (Stream responseStream = response.GetResponseStream())
        {
            // Read the bytes in responseStream and copy them to content.
            responseStream.CopyTo(content);
        }
    }

    // Return the result as a byte array.
    return content.ToArray();
}

private void DisplayResults(string url, byte[] content)
{
    // Display the length of each website. The string format
    // is designed to be used with a monospaced font, such as
    // Lucida Console or Global Monospace.
    var bytes = content.Length;
    // Strip off the "https://".
    var displayURL = url.Replace("https://", "");
    resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
}

위 방식으로 코드를 실행시 개수를 표시하기전 몇초정도 걸린수 있다.

UI스레드가 차단되어 디소스가 다운로드 될때까지 기다리게 됨.

 

2. Async 적용후 

...더보기
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class Test_AsyncTask4 : MonoBehaviour {

    public Text resultsTextBox = null;
	// Use this for initialization
	void Start () {
        SumPageSizesAsync();
    }
	
	// Update is called once per frame
	void Update () {
		
	}

    private async void SumPageSizesAsync()
    {
        // Make a list of web addresses.
        List<string> urlList = SetUpURLList();

        var total = 0;
        foreach (var url in urlList)
        {
            // GetURLContents returns the contents of url as a byte array.
            //byte[] urlContents = await GetURLContentsAsync(url);
            Task<byte[]> getContentsTask = GetURLContentsAsync(url);
            byte[] urlContents = await getContentsTask;

            DisplayResults(url, urlContents);

            // Update the total.
            total += urlContents.Length;
        }

        // Display the total count for all of the web addresses.
        resultsTextBox.text += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
    }

    private List<string> SetUpURLList()
    {
        var urls = new List<string>
    {
        "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
        "https://msdn.microsoft.com",
        "https://msdn.microsoft.com/library/hh290136.aspx",
        "https://msdn.microsoft.com/library/ee256749.aspx",
        "https://msdn.microsoft.com/library/hh290138.aspx",
        "https://msdn.microsoft.com/library/hh290140.aspx",
        "https://msdn.microsoft.com/library/dd470362.aspx",
        "https://msdn.microsoft.com/library/aa578028.aspx",
        "https://msdn.microsoft.com/library/ms404677.aspx",
        "https://msdn.microsoft.com/library/ff730837.aspx"
    };
        return urls;
    }

    private async Task<byte[]> GetURLContentsAsync(string url)
    {
        // The downloaded resource ends up in the variable named content.
        var content = new MemoryStream();

        // Initialize an HttpWebRequest for the current URL.
        var webReq = (HttpWebRequest)WebRequest.Create(url);

        // Send the request to the Internet resource and wait for
        // the response.
        // Note: you can't use HttpWebRequest.GetResponse in a Windows Store app.

        using (WebResponse response = await webReq.GetResponseAsync())

        //Task<WebResponse> responseTask = webReq.GetResponseAsync();
        //using (WebResponse response = await responseTask)
        {
            // Get the data stream that is associated with the specified URL.
            using (Stream responseStream = response.GetResponseStream())
            {
                // Read the bytes in responseStream and copy them to content.
                responseStream.CopyTo(content);

                // The previous statement abbreviates the following two statements.

                // CopyToAsync returns a Task, not a Task<T>.
                //Task copyTask = responseStream.CopyToAsync(content);

                // When copyTask is completed, content contains a copy of
                // responseStream.
                //await copyTask;
            }
        }

        // Return the result as a byte array.
        return content.ToArray();
    }

    private void DisplayResults(string url, byte[] content)
    {
        // Display the length of each website. The string format
        // is designed to be used with a monospaced font, such as
        // Lucida Console or Global Monospace.
        var bytes = content.Length;
        // Strip off the "https://".
        var displayURL = url.Replace("https://", "");
        resultsTextBox.text += $"\n{displayURL,-58} {bytes,8}";
    }
}

 

3. Task.WhenAll 적용후

...더보기
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class Test_AsyncTask4 : MonoBehaviour {

    public Text resultsTextBox = null;
	// Use this for initialization
	void Start () {
        SumPageSizesAsync();
    }
	
	// Update is called once per frame
	void Update () {
		
	}

    private async void SumPageSizesAsync()
    {
        // Make a list of web addresses.
        List<string> urlList = SetUpURLList();

        // Create a query.
        // IEnumerable<Task<int>> downloadTasksQuery = from url in urlList select ProcessURLAsync(url);
        // Use ToArray to execute the query and start the download tasks.
        // Task<int>[] downloadTasks = downloadTasksQuery.ToArray();
        // 위 방식은 GC가 너무 많이 생겨 수정
        Task<int>[] downloadTasks = new Task<int>[urlList.Count];
        for (int i=0; i<urlList.Count; i++)
        {
            downloadTasks[i] = ProcessURLAsync(urlList[i]);
        }        
        
        int[] lengths = await Task.WhenAll(downloadTasks);

        int total = lengths.Sum();

        //var total = 0;
        //foreach (var url in urlList)
        //{
        //    // GetURLContents returns the contents of url as a byte array.
        //    //byte[] urlContents = await GetURLContentsAsync(url);
        //    Task<byte[]> getContentsTask = GetURLContentsAsync(url);
        //    byte[] urlContents = await getContentsTask;

        //    DisplayResults(url, urlContents);

        //    // Update the total.
        //    total += urlContents.Length;
        //}

        // Display the total count for all of the web addresses.
        resultsTextBox.text += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
    }

    private async Task<int> ProcessURLAsync(string url)
    {
        var byteArray = await GetURLContentsAsync(url);
        DisplayResults(url, byteArray);
        return byteArray.Length;
    }

    private List<string> SetUpURLList()
    {
        var urls = new List<string>
    {
        "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
        "https://msdn.microsoft.com",
        "https://msdn.microsoft.com/library/hh290136.aspx",
        "https://msdn.microsoft.com/library/ee256749.aspx",
        "https://msdn.microsoft.com/library/hh290138.aspx",
        "https://msdn.microsoft.com/library/hh290140.aspx",
        "https://msdn.microsoft.com/library/dd470362.aspx",
        "https://msdn.microsoft.com/library/aa578028.aspx",
        "https://msdn.microsoft.com/library/ms404677.aspx",
        "https://msdn.microsoft.com/library/ff730837.aspx"
    };
        return urls;
    }

    private async Task<byte[]> GetURLContentsAsync(string url)
    {
        // The downloaded resource ends up in the variable named content.
        var content = new MemoryStream();

        // Initialize an HttpWebRequest for the current URL.
        var webReq = (HttpWebRequest)WebRequest.Create(url);

        // Send the request to the Internet resource and wait for
        // the response.
        // Note: you can't use HttpWebRequest.GetResponse in a Windows Store app.

        using (WebResponse response = await webReq.GetResponseAsync())

        //Task<WebResponse> responseTask = webReq.GetResponseAsync();
        //using (WebResponse response = await responseTask)
        {
            // Get the data stream that is associated with the specified URL.
            using (Stream responseStream = response.GetResponseStream())
            {
                // Read the bytes in responseStream and copy them to content.
                responseStream.CopyTo(content);
            }
        }

        // Return the result as a byte array.
        return content.ToArray();
    }
    
    private void DisplayResults(string url, byte[] content)
    {
        // Display the length of each website. The string format
        // is designed to be used with a monospaced font, such as
        // Lucida Console or Global Monospace.
        var bytes = content.Length;
        // Strip off the "https://".
        var displayURL = url.Replace("https://", "");
        resultsTextBox.text += $"\n{displayURL,-58} {bytes,8}";
    }
}