Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
0 / 0 / 0
Регистрация: 19.12.2022
Сообщений: 9

Смысл асинхроности в webapi

03.08.2023, 20:34. Показов 2025. Ответов 32
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет, простой вопрос в чем смысл асинхронности в .net web api , если и так на каждый запрос от клиента выделяется поток?
И если в этом потоке встречаем асинхронный код , то выделяется еще поток чтобы "отпустить" текущий?
Получается бессмысленно меняем поток на другой поток?!
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
03.08.2023, 20:34
Ответы с готовыми решениями:

WCF vs WebAPI
WCF умер, не родившись толком? В чем плюсы и в чем минусы соперников?

PATCH в WebApi 2
Нашел статью по этому поводу, вроде бы все предельно ясно, да вот что-то не так: гет работает, а при патч-запросе я получаю в ответ 204. ...

Steam WebApi и C#
Кто разбирается в Steam WebApi и C#, нужна помощь. Сам бот: https://github.com/Jessecar96/SteamBot Файл Schema.cs: ...

32
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
04.08.2023, 16:58
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Usaga Посмотреть сообщение
пока идёт ожидание асинхронной операции, поток её начавший, может быть отпущен назад в пул, откуда будет взят для обработки следующего запроса.
Кажись на этом тему можно было сворачивать.
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
04.08.2023, 17:30
Цитата Сообщение от kolorotur Посмотреть сообщение
На деле, конечно, асинхронная обработка при больших количествах одновременных запросов будет выигрывать у многопоточности и по времени обработки.
В общем по сути получается, что если у нас достаточно высоконагруженная система, где асинхронность даёт заметный выигрышь, то в ней есть смысл. В остальных ситуациях нет смысла замусоривать код.

Добавлено через 6 минут
Цитата Сообщение от Wolfdp Посмотреть сообщение
Кажись на этом тему можно было сворачивать.
Не, это вообще не ответ на исходный вопрос. Это просто обозначение одного из технических нюансов асинхронности. Ну возвращается в пул и что? Потоков что ли мало? Всегда можно новый создать.

А про смысл kolorotur и IamRain рассказали. Теперь хоть есть понимание, что в каких-то сценариях от асинхронности на сервере и в самом деле может быть польза.
0
 Аватар для IamRain
4695 / 2702 / 735
Регистрация: 02.08.2011
Сообщений: 7,236
04.08.2023, 17:42
Цитата Сообщение от kotelok Посмотреть сообщение
Теперь хоть есть понимание,
Асинхронность вообще довольно обширное понятие, прерывания в ОС - тоже элементы асинхронности.
Условно, мы работаем с вами в очной форме в одном офисе, и я знаю что для вас конфеты-леденцы - это топливо для работы.
И вот условно ближе к обеду, я могу подойти и подзарядить вас:
1. Синхронно - не смотря на то, что вы находитесь в состоянии потока, и допиливаете какую-то сложную логику, вот вот закончите; но я не смотря на ваше состояние потока, дергаю вас за плечо, и говорю, kotelok!! - возьми конфету! Подзарядил, но вывел из состояния потока. Все испортил.
2. Асинхронно, просто мимо проходя кинул пару-тройку леденцов вам на стол. Не отвлекая вас от ваших дел.
Внимание, вопрос: производительность какой системы будет выше?
1
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
04.08.2023, 17:47
Цитата Сообщение от kotelok Посмотреть сообщение
Потоков что ли мало? Всегда можно новый создать.
Ну как сказать...
В большинстве случаев ваша реализация сервиса будет работать в среде с IoC, т.е. обработкой запросов и той части, где принимается решение на создание нового потока будете управлять не вы, а веб-сервер, поверх которого вы пишете свои контроллеры (IIS, Kestrel и т.д.). Ваш код будет запускаться тогда, когда решение о создании нового потока или переиспользовании старого уже принято.
Насколько мне известно, эти сервера предполагают, что будет использоваться асинхронность, потому создают ограниченное количество потоков на обработку запросов. В этом случае без дополнительного ковыряния конфигов этих сервисов вариант с потоками будет работать совсем плохо.
Ну а чем ковыряться в документации и тестировать методом тыка, наверное проще прописать где нужно async и await.
1
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
04.08.2023, 18:17
Цитата Сообщение от kotelok Посмотреть сообщение
Не, это вообще не ответ на исходный вопрос
Если рассматривать как ответ на экзамене или собеседовании -- вполне себе годится. Если говорит об обучении... это тема явно не конкретно web api, а гораздо более раннего материала "многопоточность", где рассматривается зачем нужны отдельные потоки, почему этот ресурс дорогой, что есть операции ожидающие внешнего ресурса (начиная от чтения файла и заканчивая ожидание ответа от сокета), которые позволяют высвободить на время треад, что есть пулл и он не бесконечен (и при неправильном использовании позволят словит лок ВСЕГО приложения).
0
0 / 0 / 0
Регистрация: 19.12.2022
Сообщений: 9
04.08.2023, 19:46  [ТС]
Могу записать видео где я делаю Sleep и вызываю много раз, в дебаге вижу что каждый sleep будет в своём потоке с разными threadID
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
04.08.2023, 22:21
Цитата Сообщение от Mastodon_dev Посмотреть сообщение
Могу записать видео где я делаю Sleep и вызываю много раз, в дебаге вижу что каждый sleep будет в своём потоке с разными threadID
Эм.... спасибо, не надо. Я думаю уважаемая публика и так прекрасно понимает как работает асинхронщина.
0
0 / 0 / 0
Регистрация: 19.12.2022
Сообщений: 9
04.08.2023, 23:15  [ТС]
Wolfdp,
Миниатюры
Смысл асинхроности в webapi  
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
05.08.2023, 05:11
Mastodon_dev, я подозреваю что подразумевалось "не создается новый, а берется из пула". Сервак держит набор заготовленных тредов, для обработки входящих реквестов. Насколько знаю, тоже самое происходит и с ассинхронным кодом
- пришел запрос, выгребли из пулла свободный треад
- дошли скажем до запроса к БД, который асинхронный, ушел запрос
- тред за ненадобностью возращаеться в пул
- через N-милисекунд (ну или часов, тут как повезет) возращается ответ от БД. Из пулла опять выгребается свободный треад для формирования респонса клиента
0
Эксперт .NET
 Аватар для Usaga
14734 / 9508 / 1364
Регистрация: 21.01.2016
Сообщений: 35,879
05.08.2023, 07:39
Цитата Сообщение от kotelok Посмотреть сообщение
Не, это вообще не ответ на исходный вопрос. Это просто обозначение одного из технических нюансов асинхронности. Ну возвращается в пул и что? Потоков что ли мало? Всегда можно новый создать.
Новый поток требует памяти на стек. Это выше уже обозначали. Их нельзя бесконечно создавать. Но вообще, выше опять же верно заметили, что если у тебя сервис обслуживает полтора запроса в час, то наличие\отсутствие асинхронности решает ничего.
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
05.08.2023, 08:22
Появилось непреодолимое желание поговнокодить подтвердить свои слова кодом. А то вторая страница обсуждения и как-то грустно всё это.

Для начала посмотрим что за собой тянет пул, который держит сервак (вместо того чтобы каждый раз создавать отдельный тред). Напишем примитивный web api, который сохраняет в статическую переменную привязанную к потоку некий GUID, и всё это дело отдает как ответ на реквест.

C#
1
2
3
4
5
6
7
8
9
10
11
12
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    [ThreadStatic]
    private static Guid? val;
 
    [HttpGet]
    [Route("get")]
    public string Get() 
        => $"{Environment.CurrentManagedThreadId:D3} -- {val ??= Guid.NewGuid()}";
}
клиент, который дудосит сервак по этому url, сохраняя значения

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
using System.Collections.Concurrent;
 
await Task.Delay(TimeSpan.FromSeconds(2)); // хак, чтобы 100% успел стартануть сервак
 
await Test1();
 
Console.WriteLine("Nya");
Console.ReadKey();
 
async Task Test1()
{
    const string Url = "http://localhost:5127/test/get";
    var dic = new ConcurrentDictionary<int, ConcurrentBag<Guid>>();
 
    async Task Running()
    {
        using var client = new HttpClient();
        for (var i = 0; i < 1000; i++)
        {
            var response = await client.GetStringAsync(Url);
            var split = response.Split(" -- ");
            var id = int.Parse(split[0]);
            var guid = Guid.Parse(split[1]);
            var list = dic.GetOrAdd(id, _ => new());
            list.Add(guid);
        }
    }
 
    var tasks = new Task[50];
    for (var i = 0; i < tasks.Length; i++)
        tasks[i] = Running();
    await Task.WhenAll(tasks);
 
    var index = 0;
    foreach (var item in dic)
        Console.WriteLine($"[{index++}] {item.Key} : {string.Join("; ", item.Value.Distinct())}");
}
результат
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[0] 4 : bfba5be4-acfd-474e-b98d-7cd364390387
[1] 6 : a84415da-c764-46aa-a6f1-f905ab70936e
[2] 9 : d23c36b5-7b67-4831-a3c3-286ef8ae3fe7
[3] 10 : 959b97bb-854b-4f3e-b8b8-4f7e43e8da4d
[4] 13 : 5c79087b-62df-44f0-8f22-b756e01e0b6e
[5] 14 : b8ac4649-feee-42d2-bf3b-520c723f2aea
[6] 15 : 4e14bc57-e804-4143-97dd-836ceaf11c44
[7] 16 : 47fd6168-312f-4e22-93fa-658d37f18b84
[8] 17 : 0d813ffc-4ad9-4d49-8c74-1e63a4d1eb39
[9] 18 : b2519c73-0f34-4a26-b40c-946c36829447
[10] 19 : 2786ebe6-1712-4fe9-b649-e2245886feec
[11] 20 : f233280e-0633-454f-b0c2-427ab1d36a5c
[12] 21 : ac0eb629-818e-4781-9ae0-8b77a45b5eb7
[13] 22 : fc194294-93f5-4982-a7a1-97895f74370e
Как видим, у нас нигде не задублировалось значение, так как потоки переиспользовались. Ещё можно заметить что на 50к запросов хватило всего 14 тредов.

Теперь по перфомансу, берем "типичную" задачу: запрос, ждем 5 секунд, возвращаем некий результат. По сути типичная вычитка из БД (хотя 5с это конечно дофига). Ставим у клиента таймаут 10 сек (что вроде как двукратный запас) и пробуем запросить "одновременно" скажем 50 раз.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    private readonly static MockDB _repository = new();
 
    [HttpGet]
    [Route("get2")]
    public string Get2()
        => _repository.ReadData();
}
 
class MockDB
{
    public string ReadData()
    {
        Thread.Sleep(TimeSpan.FromSeconds(5));
        return "Nya!";
    }
}
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
using System.Collections.Concurrent;
 
await Task.Delay(TimeSpan.FromSeconds(2));
 
await Test2();
 
Console.WriteLine("Nya");
Console.ReadKey();
 
async Task Test2()
{
    const string Url = "http://localhost:5127/test/get2";
 
    var completed = 0;
    var failed = 0;
 
    async Task Running()
    {
        using var client = new HttpClient()
        { 
            Timeout = TimeSpan.FromSeconds(10)
        };
        try
        {
            var result = await client.GetStringAsync(Url);
            if (result == "Nya!")
                Interlocked.Increment(ref completed);
            else
                Interlocked.Increment(ref failed);
        }
        catch
        {
            Interlocked.Increment(ref failed);
        }
    }
 
    var tasks = new Task[50];
    for (var i = 0; i < tasks.Length; i++)
        tasks[i] = Running();
    await Task.WhenAll(tasks);
 
    Console.WriteLine($"completed - {completed}");
    Console.WriteLine($"failed    - {failed}");
}
Результат
Code
1
2
completed - 15
failed    - 35
Как видим наш простецкий сервак уже не справляется. На intel i9-8950HK (6 ядер по 2.9Гц). По ходу либо проц УГ, либо код. Попробуем асинхронный подход.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    private readonly static MockDB _repository = new();
 
    [HttpGet]
    [Route("get3")]
    public async Task<string> Get3()
        => await _repository.ReadDataAsync();
}
 
class MockDB
{
    public async Task<string> ReadDataAsync() 
    {
        await Task.Delay(TimeSpan.FromSeconds(5));
        return "Nya!";
    }
}
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
using System.Collections.Concurrent;
 
await Task.Delay(TimeSpan.FromSeconds(2));
 
await Test2();
 
Console.WriteLine("Nya");
Console.ReadKey();
 
async Task Test2()
{
    const string Url = "http://localhost:5127/test/get3";
 
    var completed = 0;
    var failed = 0;
 
    async Task Running()
    {
        using var client = new HttpClient()
        { 
            Timeout = TimeSpan.FromSeconds(10)
        };
        try
        {
            var result = await client.GetStringAsync(Url);
            if (result == "Nya!")
                Interlocked.Increment(ref completed);
            else
                Interlocked.Increment(ref failed);
        }
        catch
        {
            Interlocked.Increment(ref failed);
        }
    }
 
    var tasks = new Task[5000];
    for (var i = 0; i < tasks.Length; i++)
        tasks[i] = Running();
    await Task.WhenAll(tasks);
 
    Console.WriteLine($"completed - {completed}");
    Console.WriteLine($"failed    - {failed}");
}
Для начала рекомендую поиграть в игру "найди 10 отличий" (это к вопросу, насколько написание асинхронного кода труднее). Особенно на клиентской части, где поменялось количество запросов. Их стало слегка больше, всего каких-то х100.

Результат этого безобразия.
Code
1
2
completed - 5000
failed    - 0
МАГИЯ! Ради смеха попробовал 50к запрос (что на одном ПК несколько бессмысленно по куче причин) и даже тут занятная картина: 46к+ отработавших запросов.

Замечу что т.к. всё это крутилось на одном CPU, во-первых запросы шли не моментально, а на сколько успевал проц в конкуренции с серваком. Во-вторых, 50к запросов заходило не моментально, т.к. на клиентской части тоже есть пул (внезапно), который обрабатывает наши задачи, и что-то мне подсказывает что скорость добавления нового треда там сопоставим с теми же задержками, что и на серваке.

Ложка дегтя: сделаем ситуацию для асинхронного кода чуть реалистичнее: задержка на выполнение какой-то не асинхронной задачи и пнем всего 50 запросов.

C#
1
2
3
4
5
6
7
8
9
class MockDB
{
    public async Task<string> ReadDataAsync() 
    {
        Thread.Sleep(TimeSpan.FromSeconds(2));
        await Task.Delay(TimeSpan.FromSeconds(3));
        return "Nya!";
    }
}
Code
1
2
completed - 44
failed    - 6
Как видим магия резко испарилась, но всё ещё значительно бодрее чисто синхронного запроса. Более того, нужно понимать что асинхронные запросы появились для определенных ситуаций: когда работаем с БД (довольно часто на самом деле), запрашиваем внешнее api (реже, но вполне актуальная задача для многих проектов), либо банально что-то читаем с диска (тут профит от асинхронщины под вопросом). Ещё я не задевал вопрос распараллеливания задач. Но в общем случае у нас не всегда получится сделать запрос асинхронным по причине того что там нечему быть таковым. Банальный пример -- a+b. Эту задачу НИКАК НЕ СДЕЛАТЬ АСИНХРОННОЙ, что несколько избавляет нас от вопроса в принципе.

Проект на "потыкать лично" прилагается ниже
Вложения
Тип файла: zip DraftCheckAsync.zip (4.7 Кб, 2 просмотров)
1
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
05.08.2023, 15:04
Цитата Сообщение от Wolfdp Посмотреть сообщение
Появилось непреодолимое желание поговнокодить
Любите вы себе жизнь усложнять!

C#
1
2
3
4
5
6
7
8
9
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
 
var busyTime = TimeSpan.FromSeconds(0.1);
 
app.MapGet("/async", async () => await Task.Delay(busyTime));
app.MapGet("/thread", () => Thread.Sleep(busyTime));
 
app.Run();
Потом берем ApacheBench и стресс-тестим оба урла. Сервер можно перезапустить между тестами для чистоты эксперимента.
Эмулируем 50к запросов, разбитых на 10к одновременных соединений, т.е. в среднем будет где-то 5 запросов с одного соединения:
Windows Batch file
1
.\ab.exe -c 10000 -k -n 50000 "http://localhost:5028/thread"
Code
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
Concurrency Level:      10000
Time taken for tests:   131.593 seconds
Complete requests:      50000
Failed requests:        0
Keep-Alive requests:    50000
Total transferred:      5800000 bytes
HTML transferred:       0 bytes
Requests per second:    379.96 [#/sec] (mean)
Time per request:       26318.690 [ms] (mean)
Time per request:       2.632 [ms] (mean, across all concurrent requests)
Transfer rate:          43.04 [Kbytes/sec] received
 
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1  17.9      0     516
Processing:   521 21089 6897.5  23606   42501
Waiting:      174 21088 6897.9  23606   42501
Total:        521 21090 6897.2  23606   42501
 
Percentage of the requests served within a certain time (ms)
  50%  23606
  66%  23802
  75%  25024
  80%  25528
  90%  26522
  95%  29311
  98%  32599
  99%  35255
 100%  42501 (longest request)
Windows Batch file
1
.\ab.exe -c 10000 -k -n 50000 "http://localhost:5028/async"
Code
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
Concurrency Level:      10000
Time taken for tests:   11.003 seconds
Complete requests:      50000
Failed requests:        0
Keep-Alive requests:    50000
Total transferred:      5800000 bytes
HTML transferred:       0 bytes
Requests per second:    4544.24 [#/sec] (mean)
Time per request:       2200.589 [ms] (mean)
Time per request:       0.220 [ms] (mean, across all concurrent requests)
Transfer rate:          514.78 [Kbytes/sec] received
 
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       5
Processing:   445 1777 432.6   1887    2919
Waiting:      171 1776 432.6   1887    2898
Total:        445 1777 432.6   1887    2919
 
Percentage of the requests served within a certain time (ms)
  50%   1887
  66%   1974
  75%   2005
  80%   2044
  90%   2121
  95%   2282
  98%   2647
  99%   2782
 100%   2919 (longest request)
Тестировалось на домашнем лаптопе (Win 11 Home), а не на толковом сервере, потому вот такие задержки.
1
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
05.08.2023, 16:44
kolorotur, увы, с бренчмарками плохо знаком, да и не особо хотелось завязываться на тайминги, так как по хорошему это нужно отключать все лишнее для чистоты эксперимента (в идеале еще и второй ноут задействовать). Да и изначально предполагал что код придеться сильно сложнее писать (особенно на серверной части), чтобы достичь наглядных просадок, но оказалось всё намного веселей.

Тут больше вопрос к Mastodon_dev, остались ли у него вопрлсы по "зачем асинхроность". А то примеры это хорошо, но их код тоже нужно разбирать.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
05.08.2023, 16:44

Отправка запроса Get к WebApi
У меня есть Web Api Вот часть кода контроллера namespace WebApplication9.Controllers { &quot;)] public class...

Отправка запроса Get к WebApi
У меня есть Web Api Вот часть кода контроллера namespace WebApplication9.Controllers { &quot;)] public class...

Вставка картинки в WebApi
Хочу вставить картинку как массив байтов. Пробовала через MediaTypeFormatter, но результата нет. ApiContoller public...

WebApi + Html + css
Как в Web Api создавать фронтенд, допустим я хочу сделать веб приложение где нужно считывать данные с файлов и мне нужно, чтобы был хоть...

WebAPI случается timeout
Добрый день, коллеги. Прошу помочь разобраться в причине периодического появления тайм-аута в приложении Web API. В основном все...


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

Или воспользуйтесь поиском по форуму:
33
Ответ Создать тему
Новые блоги и статьи
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru