ZeroMQ Kill Thread nie działa

0

Siemka
Tworzę projekt z użyciem ZeroMQ i mam taki problem. Potrzebuję funkcji do startowania i ubijania dodatkowego wątku. Jak Start działa ok tak ze stopem są problemy. Poniżej kod:

 

private Thread _workerThread;
private object _locker = new object();
private bool _stop = false;

public void Start()
        {
            _workerThread = new Thread(RunZeroMqServer);
            _workerThread.Start();
        }

public void Stop()
        {
            lock (_locker)
            {
                _stop = true;
            }

            _workerThread.Join();
            Console.WriteLine(_workerThread.ThreadState);
        }

Gdzieś w internecie znalazłem też coś takiego:

 

void AsyncZmqListener::Stop()
{
    _stopped = true;
    delete _zmqContext;             // <-- Crashes the application. Changing to 'zmq_term(_zmqContext)' does not terminate recv()
    pthread_join(_thread, NULL);    // <-- This waits forever
}

ale właśnie tak jak jest napisane w komentarzach, nie działa to.

Ma ktoś może jakiś pomysł na tą funkcję stopa?

0

Mógłbyś pokazać metodę RunZeroMqServer?

0

Jasne

 

private void RunZeroMqServer()
        {
            using (var context = ZmqContext.Create())
            using (ZmqSocket server = context.CreateSocket(SocketType.REP))
            {
                /*
                var bindingAddress = new StringBuilder("tcp://");
                bindingAddress.Append(_ipAddress);
                bindingAddress.Append(":");
                bindingAddress.Append(_port);
                server.Bind(bindingAddress.ToString());
                */

                //server.Bind("tcp://192.168.0.102:50000");
                server.Bind("tcp://*:12345");

                while (!_stop)
                {
                    string message = server.Receive(Encoding.Unicode);
                    if (message == null) continue;

                    var response = ProcessMessage(message);
                    server.Send(response, Encoding.Unicode);

                    Thread.Sleep(100);
                }
            }
        }

private string ProcessMessage(string message)
        {
            ServiceRepositoryMessage srm = JsonConvert.DeserializeObject<ServiceRepositoryMessage>(message);
            string response = "";

            switch (srm.FunctionName)
            {
                case "registerService":
                    registerService(srm.ServiceName, srm.ServiceAddress, srm.BindingType);
                    return response = srm.ServiceName + " on " + srm.ServiceAddress + " using a " + srm.BindingType + " protocol was registered!";
                case "unregisterService":
                    unregisterService(srm.ServiceName);
                    return response = srm.ServiceName + " was unregistered";
                case "getServiceAddress":
                    return response = getServiceAddress(srm.ServiceName, srm.BindingType);
                case "isAlive":
                    isAlive(srm.ServiceName);
                    return response = srm.ServiceName + " is alive and kicking!";
                default:
                    return response = "Wrong service name, the only acceptable names are: registerService, unregisterService, getServiceAddress, isAlive";
            }
        }

Funkcje registerService itp. to są już funkcję operujące na bazach danych, dodające do bazy adres serwisu który się połączy z moim itd

0

Na początek pole _stop to że odwołujesz się do tej samej zmiennej nie znaczy że odwołujesz się do tego samego obszaru pamięci procesory i kompilatory robią wiele optymalizacji.
Poczytaj o Thread.MemoryBarrier i volatile

0

zamiast

Thread 
``` używaj Task ``` ``` ( zalecane przez M$ ). Klasa Task jako jeden z parametrów konstruktora przyjmuje CancelationTokenSource. Ustawiasz ten token na .cancel a w tasku w bloku Try/Catch sprawdzasz status tego tokena i w momencie gdy token ustawi się na ThrowIfCancelationRequested ( piszę z pamięci więc mogę pomylić się w nazewnictwie ) rzucany jest wyjątek a ty po prostu w Finally obsługujesz go i kończysz Task. Sprawdź czy konstruktor Task przyjmuje ten token. Jest on raczej używany do "odpal i zapomnij". Natomiast Task<t> ``` ``` na pewno jako jeden z parametrów przyjmuje token.

Możesz też użyć Backgroundworker który bazuje na Task/Thread i sam z siebie wspiera zatrzymanie pracy z podaniem wyniku tej pracy.

0

Akurat w tym przypadku wątek może okazać się lepszym rozwiązaniem od Taska. Task jest przeznaczony dla zadań które trwają kilka do kilkudziesięciu sekund w ten sposób wątek jest zwracany do puli wątków i może zostać wykorzystany ponownie. Jeżeli metoda wywoływana w Tasku będzie trwała wiele minut albo godzin, a w międzyczasie inne elementy kodu będą uruchamiać inne zadania może się okazać, że pula wątków za bardzo się rozrośnie (zwłaszcza gdy takich dużych zadań będzie kilka). A CancelationTokenSource działa też na wątkach.

0

Pozwolę sobie nie zgodzić się z tobą.

szogun1987 napisał(a):

Akurat w tym przypadku wątek może okazać się lepszym rozwiązaniem od Taska. Task jest przeznaczony dla zadań które trwają kilka do kilkudziesięciu sekund w ten sposób wątek jest zwracany do puli wątków i może zostać wykorzystany ponownie.

A skąd ta wiedza. Ja czytając o Taskach wyczytałem jedynie, że jest to następca klasy Thread i korzystanie z tego ma zapewnić kompatybilność dla programu w przyszłości po wycofaniu klasy Thread. Zresztą pod skórą Task-a i tak siedzą niektóre metody i mechanizmy z Thread-a. Nigdzie nie czytałem o takim podziale tych klas.

szogun1987 napisał(a):

Jeżeli metoda wywoływana w Tasku będzie trwała wiele minut albo godzin, a w międzyczasie inne elementy kodu będą uruchamiać inne zadania może się okazać, że pula wątków za bardzo się rozrośnie (zwłaszcza gdy takich dużych zadań będzie kilka).

Jeśli przekroczysz pulę wątków to i Taski się zblokują . Natomiast zadania będą wykonywane w kolejce bo czekają na swoją kolej w sposób podobny do SpinWait. Oczywiście czas wykonania będzie olbżymi ale .... jak chciał programista tak ma.

Skoro Task ma wykonywać się długo to przy tworzeniu proszę podać mu Schedulera z parametrem LongRunningTask.

0
wedlock napisał(a):

Ja czytając o Taskach wyczytałem jedynie, że jest to następca klasy Thread i korzystanie z tego ma zapewnić kompatybilność dla programu w przyszłości po wycofaniu klasy Thread. Zresztą pod skórą Task-a i tak siedzą niektóre metody i mechanizmy z Thread-a. Nigdzie nie czytałem o takim podziale tych klas.

A gdzie to przeczytałeś?
Task to właściwie takie opakowanie na ThreadPool, trudno to nazwać następcą Thread, bo cel trochę inny. Wycofanie klasy Thread?! Kto, kiedy i po co miałby to robić?

0

a tu : http://helion.pl/ksiazki/programowanie-rownolegle-i-asynchroniczne-w-c-5-0-mateusz-warczak-jacek-matulewski-rafal-pawlaszek,proch5.htm

autorzy książki sugerują, że Task jest "następcą" Thread bo nowinki języka 4.5 zmierzają do większego zastosowania async/await tak aby kod był przejrzysty tak jak by napisany synchronicznie. Sugerują tym, że taka jest polityka M$ i w tym kierunku zmierza język C#.

1

Klasa Task jest lepszym rozwiązaniem w przypadku gdy zadanie ma nie blokować interfejsu użytkownika albo jak w przypadku asynchronicznych akcji w kontrolerze ma za zadanie odciążyć pulę wątków zajmującą się przetwarzaniem wywołań. Możliwość korzystania z async i await też przemawia za tym żeby myśleć o Task'u zanim się pomyśli o wątku. Klasy Thread podobno nie ma w aplikacjach Windows Store (nie pisałem tych aplikacji więc nie wiem), ale z .Net jako takiego nie zostanie wyrzucona z wielu względów. Głównie przez wsteczną kompatybilność oraz fakt że zachowanie puli wątków nie zawsze jest zgodne z intencjami programistów - tych zaawansowanych.
Co do rozrostu puli wątków to z tego co pamiętam (ale mogę się mylić) jedna z jej implementacji podwaja ilość wątków gdy te się wyczerpią, respektując przy tym tylko metodę ThreadPool.SetMaxThreads.

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