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źć?
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());
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);
}));
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.
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;
}
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.
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
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.
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ę !