Подтвердить что ты не робот

Возможно ли в .NET, используя С#, создать асинхронный шаблон на основе событий без многопоточности?

Я поражен архитектурным дизайном Node.js и задавался вопросом, способен ли С# такой дизайн:

Асинхронный, основанный на событиях/цикл событий, неблокирующий I/O без многопоточности.

4b9b3361

Ответ 1

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

Тем не менее, вы можете добиться однопоточной модели асинхронного программирования, синхронизируя все операции с помощью одного потока GUI, который поддерживается для приложений Windows, используя Control.Invoke или, более общо, SynchronizationContext.

Каждый вызов BeginXyz должен быть переписан в следующих строках:

// Start asynchronous operation here (1)
var originalContext = SynchronizationContext.Current;
obj.BeginFoo(ar =>
  // Switch to the original thread
  originalContext.Post(ignored => {
    var res = obj.EndFoo(); 
    // Continue here (2)
  }));

Код, помеченный как (2), будет продолжаться в том же потоке, что и код в (1), поэтому вы будете использовать поток потока пула только для пересылки обратной копии обратно в исходный (одиночный) поток.

В качестве дополнительной заметки это более непосредственно поддерживается асинхронными рабочими процессами в F #, и его можно использовать для довольно элегантного стиля программирования графического интерфейса как описано здесь. Я не знаю node.js, но я полагаю, что вас также удивляют асинхронные рабочие процессы F #, поскольку они действительно классны для асинхронного/события на основе /... стиля программирования: -)

Ответ 2

Я работаю над такой штукой в ​​.NET как проект для домашних животных. Я называю это ALE (еще одно событие)... потому что пиво.

Это чрезвычайно альфа прямо сейчас, но все это доморощен, потому что, как и вы, я хотел знать, можно ли это сделать.

  • Это архитектура цикла событий.
  • Он использует неблокирующие асинхронные вызовы ввода/вывода .NET
  • Он использует обратные вызовы, чтобы помочь разработчикам писать асинхронный код, который немного читаем.
  • Реализация асинхронных сетевых сокетов
  • Асинхронная реализация HTTP-сервера
  • Реализация клиента Async sql

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


Пример

Следующий код:

  • Запуск цикла событий
  • Запустите веб-сервер на порту 1337
  • Запустите сервер веб-сокетов на порту 1338
  • Затем прочитайте файл и "DoSomething" с ним:
 
EventLoop.Start(() => {

    //create a web server
    Server.Create((req, res) => {
        res.Write("<h1>Hello World</h1>");
    }).Listen("http://*:1337");


    //start a web socket server
    Net.CreateServer((socket) => {
        socket.Receive((text) => {
             socket.Send("Echo: " + text);
        });
    }).Listen("127.0.0.1", 1338, "http://origin.com");


    //Read a file
    File.ReadAllText(@"C:\Foo.txt", (text) => {
        DoSomething(text);
    });
});

Итак, я думаю, что мой ответ "Да", это можно сделать на С#... или почти на любом языке. Настоящий трюк позволяет использовать собственный неблокирующий ввод-вывод.

Более подробная информация о проекте будет опубликована здесь.

Ответ 3

Конечно, для этого требуется цикл событий. Что-то вроде:

class EventLoop {
   List<Action> MyThingsToDo { get; set; }

   public void WillYouDo(Action thing) {
      this.MyThingsToDo.Add(thing);
   }

   public void Start(Action yourThing) {
      while (true) {
         Do(yourThing);

         foreach (var myThing in this.MyThingsToDo) {
            Do(myThing);
         }
         this.MyThingsToDo.Clear();
      }
   }

   void Do(Action thing) { 
      thing();
   }
}

class Program {
    static readonly EventLoop e = new EventLoop();

    static void Main() {
        e.Start(DoSomething);
    }

    static int i = 0;
    static void DoSomething() {
        Console.WriteLine("Doing something...");
        e.WillYouDo(() => {
            results += (i++).ToString();
        });
        Console.WriteLine(results);
    }

    static string results = "!";
}

Довольно скоро вы захотите избавиться от DoSomething и потребовать регистрации всех работ с помощью MyThingsToDo. Затем вам нужно передать enum или что-то в каждый ThingToDo, который сообщает ему, почему он что-то делает. В этот момент вы поймете, что у вас есть сообщение об ошибке .

Кстати, я бы сказал, node.js замалчивает тот факт, что он работает на ОС и приложении, которое многопоточно. Без этого каждый звонок в сеть или диск блокируется.

Ответ 4

Reactive Extensions for.NET (Rx) предназначен для асинхронного и параллельного программирования. Это позволяет вам программировать в интерактивном и интерактивном режиме, не блокируя. Вы используете операторы запросов LINQ и новые для интерфейсов IObservable/IObserver, которые являются частью Rx. Rx предоставляет математическое двойное значение IEnumerable/IEnumerator в виде IObservable/IObserver, что означает, что вы можете использовать все стандартные операторы запросов LINQ декларативным способом, а не напрямую использовать API многопоточности.

Ответ 5

Не уверен, что этот - это то, что вы ищете..NET может выполнять асинхронные обратные вызовы без явной многопоточности.

Ответ 6

ваш пример node.js на самом деле не применим, так как сервер, на котором он работает, выполняет все необходимые многопоточности. Если события выполняются на основе одного и того же внешнего тактового сигнала, то они не являются асинхронными. Вы можете обойти это, запустив другое приложение и создав два процесса в системе.

Невозможно, чтобы одно и то же приложение, запущенное как один процесс в одной системе, выполняло асинхронные события без другого потока.

Подробнее см. в этом вопросе Асинхронный и многопотоковый вопрос.

Ответ 7

Вы можете использовать диспетчер WPF, даже если вы не используете пользовательский интерфейс. Ссылка на сборку WindowsBase. Затем в вашем стартовом коде выполните:

Dispatcher.CurrentDispatcher.BeginInvoke(new Action(Initialize));
Dispatcher.Run();

В Initialize и в другом месте вашего кода используйте Dispatcher.CurrentDispatcher.BeginInvoke для планирования выполнения последующих методов async.

Ответ 8

i разработал сервер на основе HttpListener и цикл событий, поддерживающий MVC, WebApi и маршрутизацию. Для того, что я видел, показатели намного лучше, чем стандартные IIS + MVC, для MVCMusicStore я переместился с 100 запросов в секунду и 100% от CPU до 350 с 30% процессором. Если кто-нибудь попросит меня попробовать, я борюсь за обратную связь!

PS любое предложение или исправление приветствуется!

Ответ 9

Я считаю, что это возможно, вот пример с открытым исходным кодом, написанный на VB.NET и С#:

https://github.com/perrybutler/dotnetsockets/

Он использует Асинхронный шаблон на основе событий (EAP), Шаблон IAsyncResult и пул потоков (IOCP). Он будет сериализовать/упорядочить сообщения (сообщения могут быть любыми нативными объектами, такими как экземпляр класса), в двоичные пакеты, передать пакеты через TCP и затем десериализовать/развязать пакеты на принимающей стороне, чтобы вы могли работать с вашим родным объектом, Эта часть несколько напоминает Protobuf или RPC.

Первоначально он был разработан как "netcode" для многопользовательских игр в режиме реального времени, но он может служить многим целям. К сожалению, я никогда не использовал его. Может быть, кто-то еще будет.

Исходный код содержит много комментариев, поэтому его следует легко отслеживать. Наслаждайтесь!

EDIT: после стресс-тестов и нескольких оптимизаций он смог принять 16 357 клиентов до достижения пределов ОС. Вот результаты:

Simulating 1000 client connections over 16 iterations...
1) 485.0278 ms
2) 452.0259 ms
3) 495.0283 ms
4) 476.0272 ms
5) 472.027 ms
6) 477.0273 ms
7) 522.0299 ms
8) 516.0295 ms
9) 457.0261 ms
10) 506.029 ms
11) 474.0271 ms
12) 496.0283 ms
13) 545.0312 ms
14) 516.0295 ms
15) 517.0296 ms
16) 540.0309 ms
All iterations complete. Total duration: 7949.4547 ms

Теперь, когда все клиенты работают на localhost и отправляют небольшое сообщение на сервер сразу после подключения. В последующем тесте на другой системе сервер максимизировался при чуть более 64 000 клиентских подключений (предел порта был достигнут!) примерно в 2000 в секунду, потребляя 238 МБ ОЗУ.

Ответ 10

Вот мой пример однопоточного EAP. Существует несколько реализаций EAP в разных типах приложений: от простого консольного приложения до приложения asp.net, сервера tcp и т.д. Все они основаны на крошечной структуре под названием SingleSand, которая теоретически подключается к любому типу приложения .net.

В отличие от предыдущих ответов, моя реализация действует как посредник между существующими технологиями (asp.net, tcp сокеты, RabbitMQ) с одной стороны и задачами внутри цикла событий с другой стороны. Поэтому целью является не создание чистого однопоточного приложения, а интеграция цикла событий с существующими технологиями приложений. Я полностью согласен с предыдущей публикацией, что .net не поддерживает чистые однопоточные приложения.