Problem z wątkiem

0

Witam. Mój problem polega na zatrzymaniu programu, gdyż jeden wątek jest przyblokowany przez drugi przez co nie mogę przejść do dalszej części.

        private void Login_Click_Btn(object sender, EventArgs e)
        {
            loginbtn.IsEnabled = false;

            if (Txt_Pw.Text == "")
            {
                if (Txt_Email.Text == "E-Mail")
                {
                    CustomMsgBox.Show("Please enter nickname!");
                    loginbtn.IsEnabled = true;
                    return;
                }
                else
                {
                    //MessageBox.Show("배포용. 복돌기능 막혀잇습니다.");
                    session = MSession.GetOfflineSession(Txt_Email.Text);
                    CustomMsgBox.Show("Offline login Success : " + Txt_Email.Text);
                    MainLauncherWindow objSecondWindow = new MainLauncherWindow();
                    this.Close();
                    objSecondWindow.Show();
                }
            }
            else
            {
                var th = new Thread(new ThreadStart(delegate
                {
                    var login = new MLogin();
                    var result = login.Authenticate(Txt_Email.Text, Txt_Pw.Text);
                    if (result.Result == MLoginResult.Success)
                    {
                        CustomMsgBox.Show("Login Success : " + result.Username);
                        session = result;
                        MainLauncherWindow objSecondWindow = new MainLauncherWindow();
                        this.Close();
                        objSecondWindow.Show();
                    }
                    else
                    {
                        CustomMsgBox.Show(result.Result.ToString() + "\n" + result.Message);
                        loginbtn.IsEnabled = true;
                    }
                }));
                th.Start();
            }
        }
    }
}

title

0

Nie możesz zmieniać UI z innego wątku, niż wątek UI (tworzący kontrolki). Po co tworzysz ten drugi wątek?

0

Problem w tym, że potrzebne mi jest uzyskanie danych z TextBoxa i PasswordBoxa.
Są to dane logowania, których może używać każdą osoba i zaloguje się do gry (wiąże się to bezpośrednio z kontem Mojang).

Jedynymi wątkami którymi używane są te TextBoxy to automatyczne usuwanie z nich tekstu podstawowego.

1
Daniel Urbaniec napisał(a):

Jedynymi wątkami którymi używane są te TextBoxy to automatyczne usuwanie z nich tekstu podstawowego.

Nie mam zielonego pojęcia, pojęcia, co to znaczy, ale dopóki będziesz grzebał po kontrolkach z innego wątku, dopóty bedziesz miał wyjątek. Albo wywal wątki, albo poczytaj o Control.Invoke lub Dispatcher.Invoke, zależnie od technologii.

0

Uzyskałem pomoc.
Temat do zamknięcia.

0

Napiszę dla potomnych.

Nie można odnosić się do elementów GUI z innego wątku niż wątek główny. Tak samo z pokazywaniem komunikatów. Nawet zwykły MessageBox powinien iść przez główny wątek. Wystarczy użyć Dispatchera. A jeśli nie potrzebujesz ciągłego dostępu do kontrolek, możesz parametry przekazać do wątku, np:

** Przypadek 1 - przekazuję parametry do wątku, bo nie potrzebuję dostępu do GUI **


class LoginDto //klasa do przekazania danych, można to też zrobić w inny sposób
{
    public string Login { get; set; }
    public string Pass { get; set; }
}

void Button_Click(object sender, EventArgs args)
{
    StartOperation(loginText.Text, passText.Text);
}

void StartOperation(string login, string pass)
{
    Thread loginThread = new Thread(new ParameterizedThreadStart(Thread_DoLogin));
    LoginDto parameter = new LoginDto();
    parameter.Login = login;
    parameter.Pass = pass;

    th.Start(parameter);
}

void Thread_DoLogin(object parameters) //metoda wątku
{
    LoginDto p = parameters as LoginDto;
    //i dalej robisz sobie co chcesz
}

Przypadek 2 - zmiana elementów GUI z innego wątku - robię na przykładzie WinForms, bo jakoś mi tak wygodniej. W WPF jest bardzo podobnie, tylko że tam używa się obiektu Dispatcher


public void UpdateStatus(string msg)
{
    Action a = new Action(() => statusLabel.Text = msg);

    if(InvokeRequired)
        Invoke(a);
    else
       a();
}


void Thread_DoWork()
{
   UpdateStatus("Zaczynam robotę...");
  //robisz swoją robotę

  UpdateStatus("Kończę robotę..."); 
}

Mimo wszystko nie powinieneś też odczytywać kontrolek UI z innego wątku. Powinno się to robić analogicznie.
Małe wyjaśnienie:

InvokeRequired sprawdza, czy żądanie zostało wysłane z innego wątku niż główny (ten, który utworzył GUI). Jeśli tak, to akcja do wykonania jest przekazywana do wątku głównego (w uproszczeniu). Jeśli nie, to znaczy, że jesteśmy na wątku głównym i lecimy z koksem od razu.

W WPF funkcję InvokeRequired pełni ukryta w IntelliSense metoda CheckAccess:

if(!dispatcher.CheckAccess())
  dispatcher.Invoke...
else
...
0
Juhas napisał(a):

Napiszę dla potomnych.

Nie można odnosić się do elementów GUI z innego wątku niż wątek główny. Tak samo z pokazywaniem komunikatów. Nawet zwykły MessageBox powinien iść przez główny wątek. Wystarczy użyć Dispatchera. A jeśli nie potrzebujesz ciągłego dostępu do kontrolek, możesz parametry przekazać do wątku, np:

** Przypadek 1 - przekazuję parametry do wątku, bo nie potrzebuję dostępu do GUI **


class LoginDto //klasa do przekazania danych, można to też zrobić w inny sposób
{
    public string Login { get; set; }
    public string Pass { get; set; }
}

void Button_Click(object sender, EventArgs args)
{
    StartOperation(loginText.Text, passText.Text);
}

void StartOperation(string login, string pass)
{
    Thread loginThread = new Thread(new ParameterizedThreadStart(Thread_DoLogin));
    LoginDto parameter = new LoginDto();
    parameter.Login = login;
    parameter.Pass = pass;

    th.Start(parameter);
}

void Thread_DoLogin(object parameters) //metoda wątku
{
    LoginDto p = parameters as LoginDto;
    //i dalej robisz sobie co chcesz
}

Przypadek 2 - zmiana elementów GUI z innego wątku - robię na przykładzie WinForms, bo jakoś mi tak wygodniej. W WPF jest bardzo podobnie, tylko że tam używa się obiektu Dispatcher


public void UpdateStatus(string msg)
{
    Action a = new Action(() => statusLabel.Text = msg);

    if(InvokeRequired)
        Invoke(a);
    else
       a();
}


void Thread_DoWork()
{
   UpdateStatus("Zaczynam robotę...");
  //robisz swoją robotę

  UpdateStatus("Kończę robotę..."); 
}

Mimo wszystko nie powinieneś też odczytywać kontrolek UI z innego wątku. Powinno się to robić analogicznie.
Małe wyjaśnienie:

InvokeRequired sprawdza, czy żądanie zostało wysłane z innego wątku niż główny (ten, który utworzył GUI). Jeśli tak, to akcja do wykonania jest przekazywana do wątku głównego (w uproszczeniu). Jeśli nie, to znaczy, że jesteśmy na wątku głównym i lecimy z koksem od razu.

W WPF funkcję InvokeRequired pełni ukryta w IntelliSense metoda CheckAccess:

if(!dispatcher.CheckAccess())
  dispatcher.Invoke...
else
...

Login został całkowicie przerobiony.

        private void Login_Click_Btn(object sender, EventArgs e)
        {
            var email = Txt_Email.Text;
            var pw = Txt_Pw.Password;

            Auth(email, pw, true);
        }
1

Problem jest to, że wszystko masz upakowane w jednej funkcji.
Rzeczy, które nie powinny siedzieć w jednym pliku siedzą w jednej metodzie.
A jako, że mieszasz rzeczy to się robi spaghetti code, które w połączaniu z wielowątkowością robi takie kwiatki.

W tej klasie powinieneś tylko pobrać dane logowania i wywołać metodę na obiekcie reprezentującym usługę.

        private void Login_Click_Btn(object sender, EventArgs e)
        {
            loginbtn.IsEnabled = false;

            LogIn(Txt_Email.Text, Txt_Pw.Text);
        }

        private void LogIn(string email, string passowrd)
        {
            if (!IsVaildEmail(email))
            {
                 ....
                 return;
            }
            if (!IsValidPassword(passowrd))
            {
                 ....
                 return;
            }

            service.login(email, passowrd);
        }

Gdzie service ma delegaty wywoływane w przypadku udanego lub nie udanego logowania.
Twój kod w tej klasie ma być podtoczony do tych delegatów.

Jeśli w miejsce service.login(email, passowrd); wstawić fragment swojego kodu, to twój problem będzie w znacznym stopniu rozwiązany, polecam jednak poświęcić więcej czasu u przenieś cześć niezwiązaną z UI do Service.

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