Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.60/25: Рейтинг темы: голосов - 25, средняя оценка - 4.60
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1

Логирование при использовании многопоточности

04.09.2018, 16:12. Показов 5110. Ответов 26
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день.
Программа качает файлы из списка адресов. Делает это асинхронно. О результате пишет в лог-файл. При запуске программы постоянно вылетает эксепшен о недоступности файла, поскольку он используется другим процессом. Хотелось бы понять почему. Я предполагаю, что каждый таск пытается создать новый объект StreamWriter в своем потоке - из-за этого другие потоки теряют возможность этот файл использовать.
Вопрос:
1. Верно ли я понял причину?
2. Как заставить таски создавать объект StreamWriter всякий раз в одном и том же потоке?
3. Не будет ли у них опять взаимной блокировки и как ее избежать?

Код:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace AsyncAwaitTest
{
    class Program
    {
        public static object Threadpool { get; private set; }
 
        static void Main(string[] args)
        {
 
            List<string> urlList = new List<string>()
            {
                "http://www.uc.atr-group.ru/crl.crl",
                "http://update.formatlink.ru/1/flca002.crl",
                "http://crl.domostroitel-it.ru/public/ee293cbf63af7780277b1e7c52854e09972850e5.crl",
                "http://ca.domostroitel-it.ru/ra/cdp/ee293cbf63af7780277b1e7c52854e09972850e5.crl",
                "http://uc.itech-systems.com/crl/ITS.crl",
                "http://ca.binteh.ru/file/bintehca.crl",
                "http://gwusr.binteh.ru/cdp/bintehca.crl",
                "http://certenroll.mruc.ru/mruc2013.crl",
                "http://mruc.ntl.ru/mruc2013.crl",
                "http://mescrl.mosenergosbyt.ru/info/bd6e70a83de8e50ddeeb3c05584f06ae120ecf34.crl",
                "http://cdp.ifdk.com/ifdk-ca/cert.crl",
                "http://www.teledoc.ru/assets/documents/teledoc.crl"
 
            };
 
            DownloadAllFromUrlList(urlList);
 
        }
 
        private static void DownloadAllFromUrlList(List<string> urlList)
        {
            foreach (string url in urlList)
            {
                DownloadAsync(url);
            }
        }
 
        private static async void DownloadAsync(string url)
        {
            HttpClient client = new HttpClient();
            int rezultStringLength = 0; 
 
            try
            {
                Task<string> downloadStringTask = client.GetStringAsync(url);
 
                string rezultString = await downloadStringTask;
 
                rezultStringLength = rezultString.Length;
            }
            catch (Exception e)
            {
                Logger.Write(e.Message);
            }
 
            Logger.Write($"Строка с {url} получена. Длина строки = {rezultStringLength}");
        }
    }
 
    //Класс логгера
    internal static class Logger
    {
        internal static void Write(string text)
        {
            using (StreamWriter streamWriter = new StreamWriter($@"log.txt", append:true))
            {
                streamWriter.WriteLine(String.Format($"{DateTime.Now} : {text}"));
            }
        }
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
04.09.2018, 16:12
Ответы с готовыми решениями:

Программа вычисления выражения работает некорректно при использовании многопоточности
Нужно рассчитать выражение (см. Миниатюры). В один поток всё работает хорошо: static double Expr(uint cycleCount) { ...

Зависает при многопоточности
Доброе время суток.Помогите советом, несколько дней бьюсь, все перепробовал. Никак не могу разобраться в чем проблема, есть dll, которая...

File.OpenRead при многопоточности
Всем привет! Есть код: public class FileHandler { private static object _lock = new object(); ...

26
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
04.09.2018, 16:24
Цитата Сообщение от skitale Посмотреть сообщение
Верно ли я понял причину?
Да.

Цитата Сообщение от skitale Посмотреть сообщение
Как заставить таски создавать объект StreamWriter всякий раз в одном и том же потоке?
Никак, да и не стоит.
В классе-логгере создавайте поток при инициализации объекта, а не при каждой записи.
В методе логирования делайте замыкание и пишите в уже открытый поток.

Цитата Сообщение от skitale Посмотреть сообщение
Не будет ли у них опять взаимной блокировки?
Будет, если ее добавить.
Если не добавить, то в файле с логом будет чепуха, а не логи.

Цитата Сообщение от skitale Посмотреть сообщение
как ее избежать?
Практически никак.
Если хотите минимизировать взаимную блокировку (но не устранить полностью), то в методе логгирования пишите не сразу в файл, а просто добавляйте строк лога в какую-нибудь очередь, а отдельным потоком записывайте элементы из очереди уже в файл.
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
04.09.2018, 16:38  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
В методе логирования делайте замыкание и пишите в уже открытый поток.
Подскажите, пожалуйста,что имеется в виду? Как реализовать замыкание?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
04.09.2018, 16:53
Цитата Сообщение от skitale Посмотреть сообщение
что имеется в виду?
Синхронизация доступа к потоку записи.

Цитата Сообщение от skitale Посмотреть сообщение
Как реализовать замыкание?
Как-то так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    //Класс логгера
internal static class Logger
{
    static readonly Lazy<StreamWriter> Writer = new Lazy<StreamWriter>(InitializeStream);
    static readonly object sync = new object();
 
    private static StreamWriter InitializeStream()
    {
        var writer = new StreamWriter($@"log.txt", append: true);
        return writer;
    }
 
    internal static void Write(string text)
    {
        var writer = Writer.Value;
 
        lock (sync)
            writer.WriteLine($"{DateTime.Now} : {text}");
    }
}
Конечно, вместо статического класса я бы сделал его синглтоном и реализовал на нем IDisposable, чтобы при завершении работы приложения имелась возможность "слить" еще не записавшийся буфер логов на диск.
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
04.09.2018, 17:11  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Как-то так:
Много интересного, спасибо!

Кину себе пару ссылочек для дальнейшего прочтения - может еще кому пригодится:
Lazy - https://metanit.com/sharp/tutorial/20.1.php
Синглтон - https://metanit.com/sharp/patterns/2.3.php
IDisposable - https://habr.com/post/89720/
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
04.09.2018, 17:30
skitale, примерная реализация через синглтон:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
internal sealed class Logger : IDisposable
{
    private static readonly Lazy<Logger> instance = new Lazy<Logger>(() => new Logger());
 
    private bool disposed;
    private readonly StreamWriter writer;
    private readonly object sync = new object();
 
    public static Logger Instance => instance.Value;
 
    private Logger()
    {
        writer = new StreamWriter($@"log.txt", append: true);
    }
 
    public void Write(string text)
    {
        lock (sync)
            writer.WriteLine($"{DateTime.Now} : {text}");
    }
 
    public void Dispose()
    {
        if (disposed) return;
        if (writer != null)
            writer.Dispose();
 
        disposed = true;
    }
}
Такая реализация даст вам больше контроля над тем, когда создается экземпляр логгера и как следствие — когда открывается файл на запись, а так же позволит явно закрыть файл при завершении работы вызовом Dispose.

Экземпляр логгера получается через обращение к свойству Instance:
C#
1
2
var logger = Logger.Instance;
logger.Write("Hello world!");
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
04.09.2018, 17:52  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
public static Logger Instance => instance.Value;
Поясните, пожалуйста данную строку. Это лямбда-выражение?
Больше похоже на делегат, но нет слова "delegate". Я чет запутался

Добавлено через 1 минуту
Цитата Сообщение от kolorotur Посмотреть сообщение
() => new Logger())
Вот тут же лямбда-выражение, верно?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
04.09.2018, 17:56
Цитата Сообщение от skitale Посмотреть сообщение
Это лямбда-выражение?
Нет, это сокращенная запись свойства только для чтения:
C#
1
2
3
4
5
6
7
public static Logger Instance
{
   get
   {
      return instance.Value;
   }
}
Цитата Сообщение от skitale Посмотреть сообщение
Вот тут же лямбда-выражение, верно?
Здесь — оно, ага.
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,233
04.09.2018, 18:07
kolorotur, интересует одна деталь - а нужно ли в данном случае вбрасывать в методе Write ObjectDisposedException? Типа того:
C#
1
2
3
4
5
6
7
8
public void Write(string text)
    {
        if (disposed)
               throw new ObjectDisposedException();
 
        lock (sync)
            writer.WriteLine($"{DateTime.Now} : {text}");
    }
По идее ресурсы, которые были выделены для логирования, были утилизированы, поэтому объект больше не может выполнять свою работу.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
04.09.2018, 19:15
Цитата Сообщение от IamRain Посмотреть сообщение
а нужно ли в данном случае вбрасывать в методе Write ObjectDisposedException?
По-хорошему — да, но я думаю его StreamWriter сам выбросит
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
05.09.2018, 11:39  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
C#
1
2
3
4
5
6
7
private readonly object sync = new object();
 
public void Write(string text)
{
     lock (sync)
         writer.WriteLine($"{DateTime.Now} : {text}");
}
kolorotur, правильно ли я понял, что в приведенном выше коде мы используем нечто типа object как флаг, который будет показывать локеру, использует ли кто-то сейчас метод Write() или нет. То есть данный объект по сути дела никакого отношения к тексту программы не имеет. То есть это может быть всё что угодно, определенное в экземпляре класса Locker, однозначно его идентифицирующее, так?

И по сути дела это же получается булевый флаг - почему тогда он определен как тип object?
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2018, 12:04
Цитата Сообщение от skitale Посмотреть сообщение
правильно ли я понял, что в приведенном выше коде мы используем нечто типа object как флаг, который будет показывать локеру, использует ли кто-то сейчас метод Write()
Да, только не метод Write, а все, что внутри блока lock.
"Кто-то" в данном случае — другие потоки.
Одновременно блок lock может выполнять только один поток, все остальные будут заморожены до окончания выполнения этого блока. Поэтому лочить нужно исключительно тот код, который затрагивает общие ресурсы, при этом желательно чтобы этот код выполнялся как можно быстрее — чтобы остальные потоки меньше ждали.

Цитата Сообщение от skitale Посмотреть сообщение
То есть данный объект по сути дела никакого отношения к тексту программы не имеет.
Не понял эту фразу.
Как это не имеет? Он используется для синхронизации.

Цитата Сообщение от skitale Посмотреть сообщение
то есть это может быть всё что угодно, определенное в экземпляре класса Locker, однозначно его идентифицирующее, так?
Это может быть любая ссылка, но во избежание неприятных сюрпризов для синхронизирования лучше заводить отдельный объект, который используется только для этого.
Отсюда и приватное поле sync.

Цитата Сообщение от skitale Посмотреть сообщение
И по сути дела это же получается булевый флаг - почему тогда он определен как тип object?
Многопоточный монитор производит синхронизацию на ссылочных типах.
Булевый флаг — значимый тип, он не подходит.
1
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
05.09.2018, 14:55  [ТС]
kolorotur, большое спасибо за пояснения!

Добавлено через 2 часа 38 минут
Подскажите, пожалуйста, как грамотно прописать условие завершения программы только после завершения всех заданий (Task).
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2018, 15:07
Цитата Сообщение от skitale Посмотреть сообщение
как грамотно прописать условие завершения программы только после завершения всех заданий (Task).
Держите список активных задач и перед завершением работы вызывайте await Task.WhenAll(taskList);
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
05.09.2018, 16:00  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Держите список активных задач и перед завершением работы вызывайте await Task.WhenAll(taskList);
Что-то идет не так - программа не ждет завершения заданий, а просто закрывается после старта всех заданий.

Вот полный код:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace AsyncAwaitTest
{
    class Program
    {
        public static object Threadpool { get; private set; }
        public static List<Task> taskList;
        public static Logger logger;
 
        static void Main(string[] args)
        {
            List<string> urlList = new List<string>()
            {
                "http://www.uc.atr-group.ru/crl.crl",
                "http://update.formatlink.ru/1/flca002.crl",
                "http://crl.domostroitel-it.ru/public/ee293cbf63af7780277b1e7c52854e09972850e5.crl",
                "http://ca.domostroitel-it.ru/ra/cdp/ee293cbf63af7780277b1e7c52854e09972850e5.crl",
                "http://uc.itech-systems.com/crl/ITS.crl",
                "http://ca.binteh.ru/file/bintehca.crl",
                "http://gwusr.binteh.ru/cdp/bintehca.crl",
                "http://certenroll.mruc.ru/mruc2013.crl",
                "http://mruc.ntl.ru/mruc2013.crl",
                "http://mescrl.mosenergosbyt.ru/info/bd6e70a83de8e50ddeeb3c05584f06ae120ecf34.crl",
                "http://cdp.ifdk.com/ifdk-ca/cert.crl",
                "http://www.teledoc.ru/assets/documents/teledoc.crl"
 
            };
 
            taskList = new List<Task>();
 
            logger = Logger.Instance;
 
            DownloadAllFromUrlList(urlList);
 
            StopTheProgram();
 
 
        }
 
        public static async void StopTheProgram()
        {
            await Task.WhenAll(taskList);
 
            Console.WriteLine("Для завершения нажмите любую клавишу...");
            Console.ReadKey();
 
            logger.Dispose();
        }
 
        private static void DownloadAllFromUrlList(List<string> urlList)
        {
            foreach (string url in urlList)
            {
                logger.Write($"Начинаю качать с {url}");
                DownloadAsync(url);
            }
        }
 
        private static async void DownloadAsync(string url)
        {
            HttpClient client = new HttpClient();
            int rezultStringLength = 0; 
 
            try
            {
                Task<string> downloadStringTask = client.GetStringAsync(url);
                taskList.Add(downloadStringTask);
                string rezultString = await downloadStringTask;
                rezultStringLength = rezultString.Length;
                logger.Write($"УСПЕШНО: получил строку с {url}. Длина строки = {rezultStringLength}");
            }
            catch (Exception e)
            {
                logger.Write($"Ошибка при скачивании с {url}. Текст ошибки: {e.Message}");
            }
 
        }
    }
 
    //Класс логгера
    internal class Logger:IDisposable
    {
        private static readonly Lazy<Logger> instance = new Lazy<Logger>(() => new Logger());
 
        private bool disposed;
        private readonly StreamWriter streamWriter;
        private readonly object sync = new object();
        
        public static Logger Instance => instance.Value;
 
        public Logger()
        {
            streamWriter = new StreamWriter(@"log2.txt", append: true);
        }
 
        public void Write(string text)
        {
            lock (sync)
            {
                streamWriter.WriteLine(String.Format($"{DateTime.Now} : {text}"));
            }
        }
 
        public void Dispose()
        {
            if (disposed) return;
            if (streamWriter != null)
            {
                streamWriter.WriteLine(String.Format($"\n\n")); //для читабельности лога
                streamWriter.Dispose();
            }
 
            disposed = true;
        }
    }
}
Метод StopTheProgram() создал потому что VS не давала пометить метод Main() как async, выдавала ошибку: "Программа не содержит статического метода "Main", подходящего для точки входа"

Что-то никак в голове не уложится как работает это await/async.

Как выполнить завершение программы только когда WaitAll() завершится?

Прошу прощения за глупые вопросы... Но я что-то совсем затупил...
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2018, 16:18
Цитата Сообщение от skitale Посмотреть сообщение
async void
Перво-наперво, избегайте таких методов, если это не обработчики событий.
Если у вас метод асинхронный, то возвращайте Task, если логически он void; и Task<T>, если он возвращает T.

Рефакторинг:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
 
namespace AsyncAwaitTest
{
    class Program
    {
        public static object Threadpool { get; private set; }
        public static Logger logger;
 
        static void Main(string[] args)
        {
            List<string> urlList = new List<string>()
            {
                "http://www.uc.atr-group.ru/crl.crl",
                "http://update.formatlink.ru/1/flca002.crl",
                "http://crl.domostroitel-it.ru/public/ee293cbf63af7780277b1e7c52854e09972850e5.crl",
                "http://ca.domostroitel-it.ru/ra/cdp/ee293cbf63af7780277b1e7c52854e09972850e5.crl",
                "http://uc.itech-systems.com/crl/ITS.crl",
                "http://ca.binteh.ru/file/bintehca.crl",
                "http://gwusr.binteh.ru/cdp/bintehca.crl",
                "http://certenroll.mruc.ru/mruc2013.crl",
                "http://mruc.ntl.ru/mruc2013.crl",
                "http://mescrl.mosenergosbyt.ru/info/bd6e70a83de8e50ddeeb3c05584f06ae120ecf34.crl",
                "http://cdp.ifdk.com/ifdk-ca/cert.crl",
                "http://www.teledoc.ru/assets/documents/teledoc.crl"
 
            };
 
            logger = Logger.Instance;
 
            var taskList = DownloadAllFromUrlList(urlList);
 
            StopTheProgram(taskList);
        }
 
        public static void StopTheProgram(IEnumerable<Task> taskList)
        {
            Task.WhenAll(taskList).Wait();
 
            Console.WriteLine("Для завершения нажмите любую клавишу...");
            Console.ReadKey();
 
            logger.Dispose();
        }
 
        private static IEnumerable<Task> DownloadAllFromUrlList(List<string> urlList)
        {
            var tasks = new List<Task>();
            foreach (string url in urlList)
            {
                logger.Write($"Начинаю качать с {url}");
                tasks.Add(DownloadAsync(url));
            }
 
            return tasks;
        }
 
        private static async Task DownloadAsync(string url)
        {
            HttpClient client = new HttpClient();
            int rezultStringLength = 0;
 
            try
            {
                string rezultString = await client.GetStringAsync(url);
                rezultStringLength = rezultString.Length;
                logger.Write($"УСПЕШНО: получил строку с {url}. Длина строки = {rezultStringLength}");
            }
            catch (Exception e)
            {
                logger.Write($"Ошибка при скачивании с {url}. Текст ошибки: {e.Message}");
            }
 
        }
    }
 
    //Класс логгера
    internal class Logger : IDisposable
    {
        private static readonly Lazy<Logger> instance = new Lazy<Logger>(() => new Logger());
 
        private bool disposed;
        private readonly StreamWriter streamWriter;
        private readonly object sync = new object();
 
        public static Logger Instance => instance.Value;
 
        public Logger()
        {
            streamWriter = new StreamWriter(@"log2.txt", append: true);
        }
 
        public void Write(string text)
        {
            lock (sync)
            {
                streamWriter.WriteLine(String.Format($"{DateTime.Now} : {text}"));
            }
        }
 
        public void Dispose()
        {
            if (disposed) return;
            if (streamWriter != null)
            {
                streamWriter.WriteLine(String.Format($"\n\n")); //для читабельности лога
                streamWriter.Dispose();
            }
 
            disposed = true;
        }
    }
}
Цитата Сообщение от skitale Посмотреть сообщение
Метод StopTheProgram() создал потому что VS не давала пометить метод Main() как async
Какая версия студии? Какая версия языка установлена в настройках проекта?
Ставьте минимум 7.1 — там можно.
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
05.09.2018, 17:31  [ТС]
kolorotur, во-первых большое спасибо за ваши ответы - мне это очень помогает, а во-вторых есть несколько вопросов:

Кусок кода, который вызвал вопросы:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
        
        private static IEnumerable<Task> DownloadAllFromUrlList(List<string> urlList) 
        {
            var tasks = new List<Task>();
            foreach (string url in urlList)
            {
                tasks.Add(DownloadAsync(url)); 
            }
 
            return tasks;
        }
 
        private static async Task DownloadAsync(string url) 
        {
            HttpClient client = new HttpClient();
            int rezultStringLength = 0;
 
            try
            {
                string rezultString = await client.GetStringAsync(url);
                rezultStringLength = rezultString.Length;
                logger.Write($"УСПЕШНО: получил строку с {url}. Длина строки = {rezultStringLength}");
            }
            catch (Exception e)
            {
                logger.Write($"Ошибка при скачивании с {url}. Текст ошибки: {e.Message}");
            }
 
        }
Сами вопросы:

Почему не возвращаем просто тип List<Task>? Почему именно IEnumerable<Task>? В чем будет разница если мы вызовем только этот интерфейс?
C#
1
private static IEnumerable<Task> DownloadAllFromUrlList(List<string> urlList)


Немного дурной вопрос, наверное, но все же: во всех ли случаях в C# будет правомерна такая операция? Всегда ли выражение, передаваемое в качестве параметра будет вычисляться?
C#
1
tasks.Add(DownloadAsync(url));


Здесь мы не создаем Task в коде потому что он как бы автоматом создается самим методом? Но почему нет return?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 private static async Task DownloadAsync(string url) 
        {
            HttpClient client = new HttpClient();
            int rezultStringLength = 0;
 
            try
            {
                string rezultString = await client.GetStringAsync(url);
                rezultStringLength = rezultString.Length;
                logger.Write($"УСПЕШНО: получил строку с {url}. Длина строки = {rezultStringLength}");
            }
            catch (Exception e)
            {
                logger.Write($"Ошибка при скачивании с {url}. Текст ошибки: {e.Message}");
            }
 
        }
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2018, 17:38
Цитата Сообщение от skitale Посмотреть сообщение
Почему не возвращаем просто тип List<Task>?
Можно (предположительно — даже лучше) возвращать и список.
Личная привычка использовать минимально необходимые для работы типы: если мне не нужен функционал списка, а нужно просто перечислять элементы, то IEnumerable вполне хватает.
Меньше лишних связей получается.

Цитата Сообщение от skitale Посмотреть сообщение
во всех ли случаях в C# будет правомерна такая операция? Всегда ли выражение, передаваемое в качестве параметра будет вычисляться?
Вопрос, если честно, не понял.

Цитата Сообщение от skitale Посмотреть сообщение
Здесь мы не создаем Task в коде потому что он как бы автоматом создается самим методом?
Да, на это указывает наличие async в сигнатуре и await в теле метода.
Наличие этих слов — указание компилятору развернуть метод в полноценную асинхронную модель и возвращать из него правильный таск.

Цитата Сообщение от skitale Посмотреть сообщение
Но почему нет return?
Потому что мы из метода ничего не возвращаем — от этого и тип возвращаемого значения Task, а не Task<ЧегоНибудь>.
Если убрать из сигнатуры async, а из тела await, то придется возвращать таск.
Если изменить возвращаемый тип на Task<T>, то где-то придется возвращать экземпляр Т.
Ну а поскольку логически метод void и в сигнатуре прописан async, явно возвращать ничего не надо. Более того, это будет ошибкой компиляции.
0
4 / 5 / 1
Регистрация: 25.05.2012
Сообщений: 262
Записей в блоге: 1
05.09.2018, 17:59  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
Вопрос, если честно, не понял
Видимо вопрос совсем дурацкий Проехали.

Цитата Сообщение от kolorotur Посмотреть сообщение
Личная привычка использовать минимально необходимые для работы типы
Ага, так и подумал.

Еще момент:
Чем обусловлено использование var вместо явного List<Task>? По сути-то там должно быть IEnumerable<Task>?
C#
1
var tasks = new List<Task>();
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.09.2018, 18:04
Цитата Сообщение от skitale Посмотреть сообщение
Чем обусловлено использование var вместо явного List<Task>?
Ленью

Цитата Сообщение от skitale Посмотреть сообщение
По сути-то там должно быть IEnumerable<Task>?
Тогда невозможно будет добавлять в список запускаемые четырьмя строчками ниже таски.

Добавлено через 1 минуту
Цитата Сообщение от skitale Посмотреть сообщение
Видимо вопрос совсем дурацкий Проехали.
Нормальный вопрос, просто я не понял.
Поясните, пожалуйста.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
05.09.2018, 18:04
Помогаю со студенческими работами здесь

Ошибка при реализации многопоточности
Помогите с кодом Многопоток newthread = new Thread(funk); newthread.Start(); ...

Работа с переменными при многопоточности
Всем привет. Недавно начал работать с много поточностью и при работе с большими ресурсами (такие как List) я использую конструкцию...

Работа статического метода при многопоточности?
Всем привет, начал изучать потоки. Автор курса, который я прохожу, привел пример работы с статическими методами при многопоточности - где...

Странные символы при выводе в файл при использовании BinaryWriter
Здравствуйте, вопрос, может, тупой, но что-то я пока не догоняю. При выводе в файл: BinaryWriter o; try { ...

Как правильно передать объект в функцию при использовании многопоточности
Добрый день, форум! Возникла вот такая задача: Есть два класса: класс А, реализующий интегрирование функции, и класс В, реализующий...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это дополнительная запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru