Wątki,Obiekty i UI, czyli ogólne pytanie związane z wielowątkowością.

0

Witam,

Staram się napisać program tak aby korzystał on w dużym stopniu z wielowątkowości (mnóstwo operacji w których trzeba czekać na efekt pobrany z sieci), przy okazji traktuję to jako ćwiczenie. Mam zatem pytanie tego typu:

  1. Obiekt klasy Log został utworzony w wątku głównym (w tym samym co GUI WinFroms)
  2. Przekazuję go do obiektu który ma go użyć (aplikacja ma kilka róznych logów)
    3.Dodatkowo Log posiada zdarzenie które jest wywoływane w momencie aktualizacji jego zawartości.
 Log EmailLog = new LogUtils.Log();
 EmailLog.LogChanged += Form1_LogChanged;
 Email.EmailListener.InitEmailListener(EmailLog, "fdsf", 0, false, "sdf", "asd");
 

Metoda Form1_LogChanged utworzona w wątku głównym UI bezpośrednio aktualizuje UI w wypadku zmiany jego zawartości.
4. Wywołuję metodę statyczną InitEmailListener:

Ogólnie odpowiada ona za inicjalizację obiektu który jest singletonem i wykonuje sprawdzenie możliwości zalogowania za pomocą AttemptLogin

 
public static void InitEmailListener(LogUtils.Log Log, string hostname, int port, bool useSsl, string username, string password)
        {
            ThreadedListener = new EmailListener();
            ThreadedListener.Log = Log;
            ThreadedListener.HostName = hostname;
            ThreadedListener.Port = port;
            ThreadedListener.UseSss = useSsl;
            ThreadedListener.UserName = username;
            ThreadedListener.Password = password;

            try
            {
                ThreadedListener.AttemptLogin();
            }
            catch
            {
                ThreadedListener.Log.AddMessage("Failed to initialize EmailListener",true);
                ThreadedListener = null;
                return;
            }
        }
  1. Wywoływane jest AttemptLogin:
private async void AttemptLogin()
        {
            Log.AddMultilineMessage(string.Format("Attempt to login to:{0}",UserName),true);
            Log.AddMultilineMessage(string.Format("Hostname:{0}", HostName), false);
            Log.AddMultilineMessage(string.Format("Port:{0}", Port), false);
            Log.AddMultilineMessage(string.Format("UserName:{0}", UserName), false);
            Log.AddMultilineMessage(string.Format("Password:{0}", Password), false);
            Log.EndMultilineMessage();

            try
            {
                await AttemptLoginAsync();
            }

            catch
            {
                throw;
            }

        
        }
 
 

 private async Task AttemptLoginAsync()
        {
            using (Pop3Client Client = new Pop3Client())
                    {

                        try
                        {
                            Client.Connect(HostName, Port, UseSss);
                            Client.Authenticate(UserName, Password);
                        }
                        catch
                        {
                            Log.AddMessage("Failed in AttemptAsync");
                            throw;
                        }
              
                    }   

Teraz kilka pytań związanych z powyższym kodem:

Dane logowania zostały dobrane tak by kod na pewno zawiódł

  1. Metoda AttemptLoginAsync() jest wywoływana poprzez await więc tworzony jest dla niej nowy wątek. Który wątek - głowny czy ten utworzony na potrzeby await wykona kod z obiektu Log (AddMessage) ?

2.Zakładając że AddMessage wywołuje zdarzenie LogChanged, który wątek wykona kod UI zawarty w metodzie subskrybującej to zdarzenie. Metoda jak napisałem wyżej jest w klasie UI i wykonuje typowo operacje związane z obsługą UI.

  1. Chciałem zosragnizować god w taki spsoób by najpierw wyświetlały się infromacje związane sposobem logowania (początek metody AttemptLogin) a dopiero potem po zalogowaniu się na serwer (co zajmuje trochę czasu) był wyświetlany komunikat o powodzeniu. Niestety tak się nie dzieje - wyświetla się wszytsko na raz po odczekaniu na zalogowanie.

  2. Application.Run(Form1()) zgłasza wyjątek TargetInvocationException {"Obiekt docelowy wywołania zgłosił wyjątek."}

To mój pierwszy rproigram wielowątkowy więc prosiłbym o rozwianie moich wątpliwości.

0

Trochę pokopałem i już mam jako taki obraz sytuacji i odpowiedzi na moje pytania. Powstaje jednak inne pytanie w tym kontekście.
W jaki sposób najefektywniej w aplikacji wielowątkowej sterowac wywołaniami UI ? Czy robić to poprzez klasyczne Invoke dla kontrolki ? Czy lepiej jest zapisać sobie gdzieś (singleton) SynchronizationContext dla wątku UI i po prostu w nim za pomocą Post/Send wywołać metody odowiedzielne za aktualizację GUI ? Jak to się robi w praktyce? Bo z tego co widzę metoda z Invoke jest bardzo pracochłonna jeśli mamy więcej różnego rodzaju kontrolek, i zmiana SynchronizationContext wydaje się ciekawą alternatywą. A może jest coś jeszcze ?

0

Nie ukrywam, że konkretne odpowiedź na moje ostatnie pytanie z poprzedniego posta byłaby dla mnie bardzo pomocna. Na pewno ktoś z osób udzielających się na forum pisał coś wielowątkowego z UI.

0

Metoda AttemptLoginAsync() jest wywoływana poprzez await więc tworzony jest dla niej nowy wątek

await wykonany w wątku GUI w aplikacji WinForms NIE TWORZY NOWEGO WĄTKU.
Metoda asynchroniczna wykonuje się w tym samym wątku GUI, tylko później — po obsłużeniu bieżących komunikatów.

Wyobraź sobie to tak, że await dodaje zadanie do kolejki w wątku GUI, i zostanie ono wykonane po zakończeniu bieżącego zadania, którym zwykle jest jakieś zdarzenie, np. Form.Load czy Button.Click.

0

Dziękuję za odpowiedź. Z async sobie już poradzilem wcześniej . Bardziej chodziło mi o odpowiedź na pytanie z mojego ostatniego posta tzn gdy tworzę nowy wątek za pomocą Task. Jak to pogdodzic to z UI ?

0
W2K napisał(a):

A może jest coś jeszcze ?

Jest jeszcze komponent BackgroundWorker, który ładnie z GUI współpracuje.

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