Kilkukrotne uruchomienie tej samej metody bez blokowania wątku.

0

Nie wiem jak fachowo opisać mój problem, ale mam nadzieję, że zrozumiecie o co chodzi.
Mam metodę, której zadanie to wysłanie dyspozycji zlecenia do biura maklerskiego. W odpowiedzi dostaje numer dyspozycji tego zlecenia. Problem w tym, że w jednym czasie program może wysłać np. 5 dyspozycji, a czas odpowiedzi z biura to 2 sekundy, więc ostatnia dyspozycja wykona się po 10 sekundach (wszystkie dyspozycje uruchamiane będą szeregowo). Jak najprościej zapisać taką metodę, aby robiła swoje, bez blokowania programu uruchamiającego? No i pozostaje problem magazynowania odpowiedzi. Ja wymyśliłem to tak, że wysyłając dyspozycję, nadaję tej dyspozycji własny numer, któremu po odpowiedzi przypisuję zwrócony przez biuro iddyspozycji. Poproszę o podpowiedzi.

0

@Spine: Ok. A co się dzieje jak tą samą metodę wywołam jednocześnie asynchronicznie, a ta metoda używa statycznej klasy i metody z HttpWebRequest? Czy jednoczesne uruchomienie takiej statycznej metody jest prawidłowe, czy też powinienem użyć obiektu?

1

Próbowałeś już użyć async, await w swoim kodzie?
Coś nie działa?

0

@Spine: Jeszcze nie, ale zastanawiam się czy używanie takich statycznych metod jest prawidłowe jeśli chodzi o połączenia HTTP.

2

@duzers: https://docs.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest?view=net-6.0

We don't recommend that you use HttpWebRequest for new development. Instead, use the System.Net.Http.HttpClient class.

Widzisz, problem rozwiązany, użyj nowego sposobu :]
https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-6.0

0
Spine napisał(a):

Próbowałeś już użyć async, await w swoim kodzie?

Coś nie działa?

Raczej tego nie mogę zrobić ponieważ metoda, która uruchamia moją metodę nie może być async.

0

Bez async/await będzie ciężko jeśli rozumiem dobrze co potrzebujesz. Czy wynik musi być zwracany synchronicznie? Co jest punktem wejścia do pojedynczego procesu wysyłania dyspozycji? Jakieś UI?

0

@Aventus: Odpowiedź nie musi być synchronicznie. Najważniejsze, żeby poszła dyspozycja jak najszybciej, bo resztę jakoś przełknę. Punktem wyjścia jest plugin dll, a on kieruje się swoimi standardami. Jak dla mnie to schemat powinien wyglądać tak:

....
  pluginThread = new Thread(new ThreadStart(PluginExecutionLoop))
....

///PluginExecutionLoop
                if (backfillQueue.Count > 0 && Interlocked.Read(ref concurrentDownloads) < 5)
                {
                     // increment no of downloads
                    Interlocked.Increment(ref concurrentDownloads);

                    // dequeue the ticker and get the tickerdata for it
                     tickerData = backfillQueue.Dequeue();

                    // make an idle threadpool thread execute the download in the background
                    ThreadPool.QueueUserWorkItem(MetodaDyspozycja, tickerData);
                }
////
        private void MetodaDyspozycja(object ticker)
        {

       }

Coś w ten deseń, na podstawie jakiegoś innego projektu, który jakoś tam zakumałem i pod siebie przerobiłem.

0

Jeśli nie musi być synchronicznie to ja bym wysłał to do jakiejś kolejki, np. RabbitMQ. Miej oddzielny proces który będzie nasłuchiwał na tej kolejce i obsługiwał wszystko asynchronicznie. Wtedy w ogóle nie będzie blokował swojego głównego procesu.

Ewentualnie jeśli obrabianie tego w osobnym procesie nie wchodzi w grę, to czemu nie oddelegować tego na osobny wątek? Lub nawet odpalić Task którego nie będzie awaitował. Tylko wtedy wadą jest to że stracisz informację o błędach jeśli odpowiednio przed tym się nie zabezpieczysz, a jeśli w trakcie przetwarzania proces zostanie zamknięty to również utracisz te informacje które aktualnie przetwarzasz. Dla tego jakiś message broker i kolejka wydaje się bezpieczniejszą opcją.

2

Możesz to zrobić na wątkach, na Taskach, na ThreadPool i async/await. A nawet za pomocą TPL, Może coś w ten deseń

async Task SendRequests(int requestCount)
{
    var tasks = new List<Task>();
    for(int i = 0; i < requestCount; i++)
        tasks[i] = Task.Run(() => SendRequest()); //zauważ, że nie użwam tyutaj async/await

    var waitTask = Task.WhenAll(tasks);
    try
    {
       await waitTask;
    }
    catch(Exception ex)
    {

    }   
}

Albo z użyciem Parallel.ForEach.

Tylko jeśli Twoja metoda SendRequest jest statyczna albo nie jest po prostu thread safe, prawdopodobnie będziesz musiał zastosować jakieś locki. Ale w taki sposób jak przedstawiłem, aplikacja będzie cały czas działać. A możesz też trochę to rozbudować i wyposażyć w jakiś pasek postępu.

0

@Juhas: W tej chwili mam coś takiego:

        public Form1()
        {
            InitializeComponent();

            backfillQueue = new Queue<int>();
        
            pluginAutoResetEvent = new AutoResetEvent(false);
            pluginThread = new Thread(new ThreadStart(PluginExecutionLoop))
            {
                Name = "processing thread",
                IsBackground = true
            };

            pluginThread.Start();     
        }

      private void PluginExecutionLoop()
        {
            while (pluginIsRunning)
            {
                if (ProcessAllQueues())
                {
                    pluginAutoResetEvent.WaitOne(10); 
                }
                else
                {
                    pluginAutoResetEvent.WaitOne(10); 
                }
            }
        }

        private bool ProcessAllQueues()
        {
            try
            {
                ProcessQuotationUpdateQueue2();
                return true;
            }
            catch (Exception exception)
            {
                Debug.WriteLine("Program error. ProcessQueues exception: " + exception);
                return true;
            }
        }



        private void ProcessQuotationUpdateQueue2() 
        {
            // if there are tickers enqueued and there are less then 8 parallel downloads already running
            if (backfillQueue.Count > 0 && Interlocked.Read(ref concurrentDownloads) < 8)
            {
                // increment no of requests
                Interlocked.Increment(ref concurrentDownloads);

                // dequeue the ticker and get the tickerdata for it
                tickerData = backfillQueue.Dequeue();

                ThreadPool.QueueUserWorkItem(RefreshTicker, tickerData);
            }

        }

        private void RefreshTicker(object id)
        {
            Debug.WriteLine("\n" + "Ticker: " + id.ToString());
    
            string request= WyslijDyspozycje();

            // decrement no of downloads
            Interlocked.Decrement(ref concurrentDownloads);
        }

        int licznik = 1;

        private void button10_Click(object sender, EventArgs e)
        {
            //wyślij jednocześnie 6 dyspozycji
            do
            {
                backfillQueue.Enqueue(licznik++);
            } while (licznik < 6);
        }

Twój kod jest świetny i robi to samo co mój. Dziękuję!

Rozumiem, że poprzez tworzenia obiektu dla metody zawierającej HttpWebRequest pozbyłbym się problemów z jakimiś zgrzytami?
Pętle odpadają, ponieważ dyspozycje spływają z różnych części programu wywołującego. Po prostu program startowy dostaje sygnał, że ma uruchomić funkcję wysłania dyspozycji z jakimiś parametrami. Funkcja ta nie jest uruchamiana w innym wątku. Użytkownik sam musi o to zadbać, bo inaczej na czas "obliczeń" główny wątek programu wywołującego jest blokowany. To nie stanowi jakieś problemu jeżeli HttpWebRequest kończy zadanie po 100 ms i nie musiałbym wysyłać zadania do innego wątku, ale przy 1500 ms pracy robi ogromną różnicę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1