Reputation: 97
I'm a bit new to C# and WPF with my Project. Small picture of what i'm doing in the past. I wrote a Programm that does a backup of my MS SQL Databases. The idea was, to start every backup process in it's own process thread. That worked perfect! I start the process and thread by using:
...
string db_name = Convert.ToString(dataReader.GetValue(0));
var t = new Thread(() => BackupProcess(ServerName, SQLinstance, BackupPath, db_name));
t.Start();
Now, when all comes to an end, i would like to Monitor like "If the last thread comes to end, than do something like XY" What is the best way do this? THX for answering
Upvotes: 2
Views: 231
Reputation: 97
Ok, i do not really understand the way it goes. As you can see, i'm going to start a new Task inside my Loop at Point: "while (dataReader.Read())". As much databases as i have in my Server, the Loop will repeat. I don't know how many DB's i will have in the next time. At the end, when ALL Tasks are finished, i would like to go to the "DoSomething" Method. Is there anybody who can tell me the way it goes. It makes me crazy (this stuff). :-)
private async void ReadDatabase()
{
string errorMSG01 = "...";
string errorMSG02 = "...";
string captionMSG = "...
string BackupPath = txtBakPath.Text;
string ServerName = txtSqlServer.Text;
string SQLinstance = cmbSqlInst.SelectedItem.ToString();
MessageBoxButtons buttons = MessageBoxButtons.OK;
if (String.IsNullOrEmpty(txtBakPath.Text))
{
System.Windows.Forms.MessageBox.Show(errorMSG01, captionMSG, buttons, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
else if (!Directory.Exists(BackupPath))
{
System.Windows.Forms.MessageBox.Show(errorMSG02, captionMSG, buttons, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
else
{
string connetionString = null;
SqlConnection connection;
SqlCommand command;
string sql = null;
SqlDataReader dataReader;
connetionString = ("Data Source=DPC-OHEINRICH\\NETZWERKLABOR; Integrated Security=True;");
sql = "Select * from Sys.Databases";
connection = new SqlConnection(connetionString);
try
{
connection.Open();
command = new SqlCommand(sql, connection);
dataReader = command.ExecuteReader();
StatusPrinter(0x0003, null);
CreateLogfile(BackupPath);
DeleteOldBakFiles();
while (dataReader.Read())
{
string db_name = Convert.ToString(dataReader.GetValue(0));
Task backupTask = Task.Run(() => BackupProcess(ServerName, SQLinstance, BackupPath, db_name));
}
dataReader.Close();
command.Dispose();
connection.Close();
}
catch (Exception ex)
{
lblStatusMessage.Content = Convert.ToString(ex.Message);
}
}
}
private void BackupProcess(string ServerName, string SQLinstance, string BackupPath, string db_name)
{
DateTime today = DateTime.Now;
string todayformat = string.Format("{0:G}", today);
string BakFileDate = todayformat.Replace(".", "").Replace(" ", "").Replace(":", "");
string ReportBakFile = BackupPath + "\\" + db_name + "_db_" + BakFileDate + @".bak'";
if (db_name != "tempdb")
{
string connetionString = null;
SqlConnection connection;
SqlCommand command;
string sql = null;
SqlDataReader dataReader;
connetionString = ("Data Source="+ServerName+"\\"+SQLinstance+"; Integrated Security=True;");
sql = @"BACKUP DATABASE " + db_name + @" TO DISK = '" + BackupPath + "\\" + db_name + "_db_" + BakFileDate + @".bak'";
connection = new SqlConnection(connetionString);
try
{
connection.Open();
command = new SqlCommand(sql, connection);
dataReader = command.ExecuteReader();
dataReader.Close();
command.Dispose();
connection.Close();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
this.Dispatcher.Invoke(new Action(() => this.WriteLogile(1, ReportBakFile)));
this.Dispatcher.Invoke(new Action(() => this.StatusPrinter(0x0004, db_name)));
}
}
public void DoSomething()
{
//we are done...
}
Upvotes: 0
Reputation: 5322
Have a look at the TPL and the ContinueWith. It's very elegant and should match your requirements.
Action<string> action =
(str) =>
Console.WriteLine("Task={0}, str={1}, Thread={2}", Task.CurrentId, str, Thread.CurrentThread.ManagedThreadId);
// Creating a sequence of action tasks (that return no result).
Console.WriteLine("Creating a sequence of action tasks (that return no result)");
Task.Factory.StartNew(() => action("alpha"))
.ContinueWith(antecendent => action("beta")) // Antecedent data is ignored
.ContinueWith(antecendent => action("gamma"))
.Wait();
from: https://msdn.microsoft.com/de-de/library/dd321405(v=vs.110).aspx
ContinueWith() also can help you handle exceptions that occured while executing the task.
Upvotes: 0
Reputation: 73462
Almost there is no reason to directly deal with threads now. It is fairly easy to do with Task Parallel Library and async-await.
You start number of Tasks, keep the reference of tasks, and wait/await it when needed.
Task backupTask = Task.Run(() => BackupProcess(ServerName, SQLinstance, BackupPath, db_name));
Task someOtherTask = Task.Run(() => SomeOtherWork(anyParameter));
...
//Later at some point
await Task.WhenAll(new []{backupTask, someOtherTask });
//At this point all tasks has been completed
//Do whatever you need to execute after all tasks finished
Note: BackupProcess
can be made asynchronous if you use asynchronous API of your data provider without the use of Task.Run
which makes a ThreadPool thread to wait there.
If you still wanted to go with Thread approach(which you shouldn't), you can use CountDownEvent
for synchronization or Thread.Join
on all the started threads.
Upvotes: 2