jak nie zwieszać programu podczas wykonywania się czasochłonnych operacji?

0

Witajcie!
Mam metody do wykonania, które pobierają dane z bazy danych. Zajmuje to chwilę.
Przed wywołaniem metody daję UserControl.Visibility = Visible, a po skończeniu metody daję Hidden.
Niestety UserControl się nie pokazuje w ogóle, a jak usunę znikanie kontrolki po skończeniu metody pojawia mi się ona po ukończeniu metody.
Chcę opakować całą metodę tak, aby wywołała się w osobnym wątku, jednak wtedy UserControl pojawi się i zniknie nie bacząc na to, że wątek jeszcze trwa. Znowu "czekanie na zakończenie wątku" zawiesi mi program. Jak to ugryźć?

2

Programowanie wielowątkowe - to jest odpowiedź. Możesz to uczynić bardzo łatwo przy pomocy Task.Run() i Task.ContinueWith() przekazując context z wątku GUI, tak aby móc odświeżyć elementy GUI.
Przykład:

	    var tasks = new List<System.Threading.Tasks.Task>();
            tasks.Add(Task.Run(() => /* Tutaj operacje, które zajmują wiele czasu */));
   	    tasks.Add(Task.Run(() => /* Tutaj operacje, które zajmują wiele czasu */));
            tasks.Add(Task.Run(() => /* Tutaj operacje, które zajmują wiele czasu */));
            
            System.Threading.Tasks.Task.Factory.ContinueWhenAll(tasks.ToArray(), _tasks =>
            {
                // np.: this.Control.Enable = true;
		/ * Tutaj odświeżasz GUI */
            }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
0

Hm, tylko jak moja czasochłonna operacja (metoda) chce mi coś zwrócić to robi się nieciekawie :(

tasks.Add(Task.Run(() => {
                string server = "guag.cba.pl",
                db = "gswidwa",
                id = "gswidwa",
                pass = "";
                string mojePolaczenie =
                "SERVER=" + server + ";" +
                "DATABASE=" + db + ";" +
                "UID=" + id + ";" +
                "PASSWORD=" + pass + ";";
                return new MySqlConnection(mojePolaczenie);
            })); 
0

Nie prawda. Po pierwsze ta metoda nie powinna zwracać nic w postaci *SqlConnection, tylko gotowy zestaw danych w postaci ViewModeli/kolekcji. Pamiętaj, że otrzymujesz do delegatu tablicę Task'ów, które mają właściwość Result, dzięki niej możesz dobrać się do zwracanego wyniku.

5

Przy pomocy asnc i await (net4.5) osiągniesz to samo i znacznie prośćiej ;)

 private async void Button_Click(object sender, RoutedEventArgs e)
        {
            (sender as Button).Visibility = System.Windows.Visibility.Hidden;
            int a = await Task.Run<int>(() => CiezkaMetoda());
            (sender as Button).Visibility = System.Windows.Visibility.Visible;
        }

        public int CiezkaMetoda()
        {
            Thread.Sleep(6666);
            return 0;
        }
 
0

Korzystasz z jakieś biblioteki do obsługi tej bazy? Może ma ona asynchronicznie API?

W przypadku operacji sieciowych lepsze jest podejście asynchroniczne, a nie tworzenie wątków/tasków, no chyba że nie zapewnia tego biblioteka to wtedy najprostszym rozwiązaniem jest chyba Task.Run.

1

Witam,

Dla SQL Server możesz zrobić coś takiego:

namespace ProjectName
{
    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Windows.Forms;

    public partial class frmMain : Form
    {
        private readonly DataTable dataTable = new DataTable();

        private SqlConnection connection;

        public frmMain()
        {
            this.InitializeComponent();
        }

        private void btnGet_Click(object sender, System.EventArgs e)
        {
            var connectionString = @"connectionString";

            this.connection = new SqlConnection(connectionString);
            this.connection.Open();

            var command = this.connection.CreateCommand();

            command.CommandText = "query";
            command.CommandTimeout = 600;

            var callback = new AsyncCallback(this.HandleCallback);
            command.BeginExecuteReader(callback, command);
        }

        private void HandleCallback(IAsyncResult result)
        {
            var command = (SqlCommand)result.AsyncState;

            using (var reader = command.EndExecuteReader(result))
            {
                this.dataTable.Load(reader);
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new Action(
                                                 () =>
                                                     {
                                                         this.dgvMain.DataSource = this.dataTable;
                                                         this.dgvMain.Refresh();
                                                     }));
                }
                else
                {
                    this.dgvMain.DataSource = this.dataTable;
                    this.dgvMain.Refresh();
                }
            }
        }
    }
}

Pozdrawiam,

mr-owl

3

Radzę programować asynchronicznie.
Większość początkujących rzuca się na programowanie wielowątkowe, które tylko wygląda na proste rozwiązanie.
Kończy się na race condtion albo dead lock, które trudno jest odtworzyć i/lub przeanalizować.

Wielu już widziałem, którym się wydawało, że wiedzą, jak programować wielowątkowo.

Poza tym, często widzę, że wątki są używane na wyrost.

0

Koledzy dziękuję Wam wszystkim z takie zainteresowanie tematem i całą pomoc. Analizując wszystko ułożyłem działający kod:
Kod kontrolki:

private bool validateLogin;
        public bool ValidateLogin
        {
            get
            {
                return validateLogin;
            }
            set
            {
                validateLogin = value;
                if (validateLogin == true)
                    hLogin.Background = Brushes.Green;
                else
                    hLogin.Background = Brushes.Red;
                OnPropertyChanged("ValidateLogin");
            }
        }
        private async void  CheckLoginValidating()
        {
            Database.WaitingControl.Visible = true;
            ValidateLogin = await Task.Run<bool>(() => Database.Account_LoginExist(
                    this.Dispatcher.Invoke<string>(() => hLogin.Text)
                ));
            Database.WaitingControl.Visible = false;
        } 

Teraz wezmę się za analizowanie dokładnie co tam żeście natworzyli, bo niektórych rzeczy jeszcze nie rozumiem :) Korzystam do łączenia się z bazą danych za pomoca MySQL

Jeszcze raz dziekuję !

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