async и await при добавлении элементов в List‹T›

Я написал метод, который добавляет в список элементы из множества источников. Смотри ниже:

public static async Task<List<SearchingItem>> GetItemsToSelect()
    {
        List<SearchingItem> searchingItems = new List<SearchingItem>();

        foreach (Place place in await GetPlaces())
        {
            searchingItems.Add(new SearchingItem() { 
                IdFromRealModel=place.Id, NameToDisplay=place.FullName, 
                ExtraInformation=place.Name, TypeOfSearchingItem=TypeOfSearchingItem.PLACE });
        }

        foreach (Group group in await GetGroups())
        {
            searchingItems.Add(new SearchingItem()
            {
                IdFromRealModel = group.Id, NameToDisplay = group.Name,
                ExtraInformation = group.TypeName, TypeOfSearchingItem = TypeOfSearchingItem.GROUP
            });
        }

        return searchingItems;
    }

Я проверил этот метод и работает правильно. Я предполагаю, что это работает правильно, потому что метод GetPlaces возвращает 160 элементов, а GetGroups возвращает 3000. Но мне было интересно, будет ли это работать, если методы возвращают элементы одновременно. . Должен ли я заблокировать список searchItems?

Спасибо за совет.


person piotrbalut    schedule 04.10.2014    source источник
comment
Should I lock list searchingItems, Нет, так как второй цикл не будет выполнен до завершения первого.   -  person L.B    schedule 05.10.2014


Ответы (1)


Ваши элементы не запускаются одновременно, вы запускаете GetPlaces(), останавливаетесь и ждете результата GetPlaces(), затем переходите к первому циклу. Затем вы запускаете GetGroups(), останавливаетесь и ждете результата GetGroups(), затем переходите ко второму циклу. Ваши циклы не являются параллельными, поэтому вам не нужно блокировать их при их добавлении.

Однако, если вы заметили, что ваши два асинхронных метода также не являются параллельными, вы можете легко изменить свою программу, чтобы сделать это так.

public static async Task<List<SearchingItem>> GetItemsToSelect()
{
    List<SearchingItem> searchingItems = new List<SearchingItem>();
    var getPlacesTask = GetPlaces();
    var getGroupsTask = GetGroups();

    foreach (Place place in await getPlacesTask)
    {
        searchingItems.Add(new SearchingItem() { 
            IdFromRealModel=place.Id, NameToDisplay=place.FullName, 
            ExtraInformation=place.Name, TypeOfSearchingItem=TypeOfSearchingItem.PLACE });
    }

    foreach (Group group in await getGroupsTask)
    {
        searchingItems.Add(new SearchingItem()
        {
            IdFromRealModel = group.Id, NameToDisplay = group.Name,
            ExtraInformation = group.TypeName, TypeOfSearchingItem = TypeOfSearchingItem.GROUP
        });
    }

    return searchingItems;
}

Это будет запускать GetPlaces(), запускать GetGroups(), останавливаться и ждать результата GetPlaces(), затем переходить к первому циклу, останавливаться и ждать результата GetGroups(), затем переходить ко второму циклу.

Два цикла по-прежнему не параллельны, но ваши два метода ожидания могут дать вам небольшой прирост производительности. Я сомневаюсь, что вы получите какую-либо выгоду от одновременного выполнения циклов, они, похоже, просто строят модели, и накладные расходы на обеспечение его потокобезопасности не будут стоить того, как мало работы выполняется.

Если вы действительно хотите попробовать сделать его более параллельным (но я сомневаюсь, что вы увидите большую пользу), используйте PLINQ для построения своих моделей.

public static async Task<List<SearchingItem>> GetItemsToSelect()
{
    var getPlacesTask = GetPlaces();
    var getGroupsTask = GetGroups();

    var places = await getPlacesTask;

    //Just make the initial list from the LINQ object.
    List<SearchingItem> searchingItems = places.AsParallel().Select(place=>
        new SearchingItem() { 
            IdFromRealModel=place.Id, NameToDisplay=place.FullName, 
            ExtraInformation=place.Name, TypeOfSearchingItem=TypeOfSearchingItem.PLACE 
        }).ToList();

    var groups = await getGroupsTask;

    //build up a PLINQ IEnumerable
    var groupSearchItems = groups.AsParallel().Select(group=>
        new SearchingItem()
        {
            IdFromRealModel = group.Id, NameToDisplay = group.Name,
            ExtraInformation = group.TypeName, TypeOfSearchingItem = TypeOfSearchingItem.GROUP
        });

    //The building of the IEnumerable was parallel but the adding is serial.
    searchingItems.AddRange(groupSearchItems);

    return searchingItems;
}
person Scott Chamberlain    schedule 04.10.2014