I am creating a task and then assign a thread that waits for the task to be completed and then modify the UI.
string txt = txtHelloMessage.Text;
HelloTask = Task<string>.Factory.StartNew(
() =>
{
string msg = client.SayHello(txt);
return msg;
}
);
new Thread(
() =>
{
while (true)// waiting for completion, I think that this is wrong
{
if (HelloTask.IsCompleted)
{
this.Dispatcher.Invoke((Action)delegate() { txtHelloMessage.Text = HelloTask.Result; });
break;
}
}
}
).Start();
Is this good practice?
Since you're using .NET 4.5 / C# 5, you could use the await keyword. Here's a really stupid, simple example:
private async void button1_Click(object sender, EventArgs e)
{
await GetSomeTextForSomeReason();
}
private async Task GetSomeTextForSomeReason()
{
var s = await Task.Factory.StartNew(() =>
{
for (int i = 0; i < 500000000; i++) ; // simulate a delay
return "This is text";
});
textBox1.Text = s;
}
No, it's not good practice.
You're using a very heavy and expensive Thread to handle the outcome of a lightweight (cheap) Task.
In this case you could just let the Task itself do the Invoke(), or use a continuation.
But the code looks very artificial, maybe post something closer to your real problem.
Related
I am a newbie with C#. I am trying to get a task to run in parallel to a stored procedure execution. So for example - I have a stored procedure which in this instance will just be running WAITFOR DELAY '00:00:10'. During that ten seconds a label will be populated with text which will have full stops added and then remove as you would see in a loading screen.
Disabling Replication.
Disabling Replication..
Disabling Replication...
Once the proc is completed it will break out of the while loop. This is being handled by a boolean value. So _IsRunning = false, run stored procedure then set IsRunning = true. The code works exactly as I would want it to if I just use Thread.Sleep(1000), which I used for test purposes, instead of a stored procedure.
Can someone tell me why this will not work when using the stored procedure? It just gets stuck on the while loop and constantly says Disabling Replication with the loading full stops.
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AsynchronousCoding
{
public partial class Form1 : Form
{
private DataAccess _access = new DataAccess();
public Form1()
{
InitializeComponent();
}
private async Task LoadRapport()
{
await DisableReplication();
}
private async Task DisableReplication()
{
Task.Factory.StartNew(TestMethod);
await Task.Factory.StartNew(() => ShowProgressText("Disabling Replication"));
}
private bool _IsRunning;
private void TestMethod()
{
_isRunning= false;
//Thread.Sleep(10000);
_access.Rapport_ReplicationSetting();
_isRunning= true;
}
private void ShowProgressText(string txt)
{
var count = 0;
var logText = new StringBuilder();
logText.Append(txt);
var baseLen = logText.Length;
while (!_isRunning)
{
Thread.Sleep(250);
if (count >= 3)
{
logText.Remove(baseLen, count);
count = 0;
}
logText.Append(".");
count++;
BeginInvoke(new Action(() => { UpdateProgressText(logText.ToString()); }));
}
BeginInvoke(new Action(() => { UpdateProgressText(txt + " - Complete"); }));
Thread.Sleep(2000);
}
private void UpdateProgressText(string txt)
{
lblProgress.Text = txt;
}
private void button1_Click(object sender, EventArgs e)
{
LoadRapport();
}
}
}
Consider this simple example with CancellationToken usage. I don't suggest bool, it's not Thread-safe.
private async void button1_Click(object sender, EventArgs e)
{
IProgress<string> progress = new Progress<string>(s => lblProgress.Text = s);
using (CancellationTokenSource cts = new CancellationTokenSource());
{
Task animationTask = ProgressAnimationAsync(progress, cts.Token);
await DoSomeJobAsync();
// await Task.Run(() => DoSomeHeavyJob()); // uncomment for test
cts.Cancel();
await animationTask;
}
}
private async Task ProgressAnimationAsync(IProgress progress, CancellationToken token)
{
int i = 1;
while (!token.IsCancellationRequested)
{
progress.Report("Loading" + new string("." , i));
i = i == 3 ? 1 : i + 1;
await Task.Delay(250);
}
}
// as I/O-bound operation
private async Task DoSomeJobAsync()
{
await Task.Delay(10000);
}
// as CPU-bound operation
private void DoSomeHeavyJob()
{
Thread.Sleep(10000);
}
There's two different methods, you can test both.
Note: it's safe to use lblProgress.Text directly here, without IProgress synchronized callback. It's given here just for example. Create new Progress in UI Thread and you may call .Report() from any other Thread safely without Invoke or BeginInvoke.
You can learn more about X-bound operations here.
I have the following threaded code ( i think ) form with a start and cancel button and a multilined text box, the commented sections (//) are from the working single threaded version and below i have tried to retrofit the multithreaded task.factory parts, however it seems to launch the powershell commands fine ( show in task manager ) but the program completes without waiting for the results from each "heavyOperation".
The idea is to start all the four HeavyOperation tasks at the same time (ish) and wait for each to return the results and append the results to the text box
public partial class Form1 : Form
{
Progress<string> progressReporter = new Progress<string>();
CancellationTokenSource cancelSource;
public Form1()
{
InitializeComponent();
progressReporter.ProgressChanged += progressManager_ProgressChanged;
}
async private void btnStart_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
btnCancel.Enabled = true;
cancelSource = new CancellationTokenSource();
//textBox1.Text = await Task.Run(() => PerfromTaskAction(cancelSource.Token));
await Task.Run(() => PerfromTaskAction(cancelSource.Token));
lblStatus.Text = "Completed."; btnStart.Enabled = true;
btnCancel.Enabled = false;
}
//private string PerfromTaskAction(CancellationToken ct)
static void PerfromTaskAction(CancellationToken ct)
{
//StringBuilder sb = new StringBuilder();
object[] arrObjects = new object[] { "SERVER1", "SERVER2", "SERVER3", "SERVER4" };
foreach(object i in arrObjects)
{
//if (ct.IsCancellationRequested) break;
//sb.Append(string.Format("{0}: {1}\r\n", HeavyOperation(i.ToString()),i));
//((IProgress<string>)progressReporter).Report(string.Format("Now Checking: {0}...", i));
Task.Factory.StartNew(() => HeavyOperation(i.ToString()));
}
//return sb.ToString();
}
void progressManager_ProgressChanged(object sender, string e)
{
lblStatus.Invoke((Action)(() => lblStatus.Text = e));
}
//private string HeavyOperation(string i)
public static void HeavyOperation(string i)
{
PowerShell ps = PowerShell.Create();
ps.AddCommand("invoke-command");
ps.AddParameter("computername", i);
ps.AddParameter("scriptblock", ScriptBlock.Create("get-vmreplication | select State"));
Collection<PSObject> result = ps.Invoke();
//return result[0].Properties["State"].Value.ToString();
Console.Write(result[0].Properties["State"].Value.ToString());
}
private void btnCancel_Click(object sender, EventArgs e)
{
cancelSource.Cancel();
}
}
Thanks for looking
You also need to await for the HeavyOperations to complete.
You can use Task.WhenAll for this purpose. Here is an async version of PerformTaskAction, using Task.WhenAll
I've taken into account Scott Chamberlain's suggestions:
Changed the unsafe (in async-await context) Task.Factory.StartNew() to Task.Run()
Removed the unnecessary await in the invocation of PerformTaskAction()
Passed the missing CancellationToken in the outer Task.Run() call
static async Task PerfromTaskAction(CancellationToken ct) {
//StringBuilder sb = new StringBuilder();
object[] arrObjects = new object[] { "SERVER1", "SERVER2", "SERVER3", "SERVER4" };
IList<Task> tasks = new List<Task>(); // collect all tasks in single collection
foreach( object i in arrObjects ) {
//if (ct.IsCancellationRequested) break;
//sb.Append(string.Format("{0}: {1}\r\n", HeavyOperation(i.ToString()),i));
//((IProgress<string>)progressReporter).Report(string.Format("Now Checking: {0}...", i));
tasks.Add(Task.Run(() => HeavyOperation(i.ToString())));
}
await Task.WhenAll(tasks).ConfigureAwait(false); // wait asynchronously for all tasks to complete
}
Now that PerformTaskAction is async, you also need to await on it.
Finally you invoke PerformTaskAction, making sure you also pass the CancellationToken.
await Task.Run( ()=> PerformTaskAction(cancelSource.Token), cancelSource.Token);
I have the need to move some processes to async. I have 5 methods that I need to call individually and have run in the background so the user can continue on with their work.
The test code below seems to work... but I haven't been able to figure out how to return information (message) indicating that the a task has completed. The class will be called from a separate windows form so that the progress can be displayed....
from the form:
async void BtnGo_Click(object sender, System.EventArgs e)
{
label2.Text = #"Starting tasks...";
var progress = new Progress<string>(
p =>
{
label2.Text = p;
});
await TestTask.MyTestMain(progress);
}
the class:
public static class TestTask
{
public static Task MyTestMain(IProgress<string> pProgress)
{
return SomethingAsync(pProgress);
}
private static async Task SomethingAsync(IProgress<string> pProgress)
{
var t1 = SomeThing1(pProgress);
var t2 = SomeThing2(pProgress);
await Task.WhenAll(t1, t2);
if (pProgress != null) pProgress.Report(#"all tasks completed");
}
private static async Task SomeThing1()
{
await Task.Delay(9000);
var filename = #"c:\temp\tt1.txt";
if (File.Exists(filename))
File.Delete(filename);
using (TextWriter tw = new StreamWriter(filename))
{
await tw.WriteLineAsync(DateTime.Now.ToLongDateString());
}
if (pProgress != null) pProgress.Report(#"t1 completed");
}
private static async Task SomeThing2()
{
await Task.Delay(7000);
var filename = #"c:\temp\tt2.txt";
if (File.Exists(filename))
File.Delete(filename);
using (TextWriter tw = new StreamWriter(filename))
{
await tw.WriteLineAsync(DateTime.Now.ToLongDateString());
}
if (pProgress != null) pProgress.Report(#"t2 completed");
}
}
I would like know when each task has completed. Any help or direction would be appreciated.
EDIT
I have edited this post to reflect my changes... I still cannot get a progress report back to the UI... any thoughts?
You're doing IO bound work, you don't need to use thread-pool threads.
Transform your methods to use the async APIs of StreamWriter:
private static async Task FirstThingAsync()
{
var filename = #"c:\temp\tt1.txt";
if (File.Exists(filename))
File.Delete(filename);
using (TextWriter tw = new StreamWriter(filename))
{
await tw.WriteLineAsync(DateTime.Now);
}
}
Same for your second method, and then you can asynchronously wait on them concurrently:
private static async Task SomethingAsync()
{
var firstThing = FirstThingAsync();
var secondThing = SecondThingAsync();
await Task.WhenAll(firstThing, secondThing);
}
Edit:
You're never reaching your first Progress.Report call because your code is throwing an InvalidOperationException when you call t.Start() on a promise-style task:
t1.Start();
await t1;
t2.Start();
await t2;
The task returned from both method calls is a "hot task", meaning it's operation is already started. The docs on Task.Start say:
InvalidOperationException: The Task is not in a valid state to be
started. It may have already been started, executed, or canceled, or
it may have been created in a manner that doesn't support direct
scheduling.
The reason you're not seeing that exception is because you're swallowing it:
var t = SomethingAsync(pProgress);
When you don't await on the async operation. Your method calls should look like this:
public static Task MyTestMain(IProgress<string> pProgress)
{
return SomethingAsync(pProgress);
}
async void BtnGo_Click(object sender, System.EventArgs e)
{
label2.Text = #"Starting tasks...";
var progress = new Progress<string>(
p =>
{
label2.Text = p;
});
await TestTask.MyTestMain(progress);
}
I want run a thread continuously. This thread would poll and check for card status. Here is a sample implementation:
static void Main(string[] args)
{
var _cancelationTokenSource = new CancellationTokenSource();
new Task(() => chkRequestTask(_cancelationTokenSource), _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
while (true)
{
}
}
static bool chkRequestTask(CancellationTokenSource _cancellationTokenSource)
{
bool noRequest = false;
while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
var RequestTask = Task.Factory.StartNew(() => noRequest = chkRequestTask(_cancellationTokenSource), _cancellationTokenSource.Token);
if (noRequest)
{
_cancellationTokenSource.Token.WaitHandle.WaitOne(15000);
Console.WriteLine("Waiting for 15Seconds");
}
else
{
Console.WriteLine("Checking the card");
}
}
return noRequest;
}
What I want to achieve here is chkRequestTask should be run on a separate thread. This would continously poll the status of the card. For this sample I'm simply doing : Console.WriteLine("Checking the card");.
Once it checks the status of the card it should sleep for 15secs for this sample only (in general it should check every 50ms, but for testing purposes I have kept 15secs).
But in the above sample it's not sleeping it's simply giving me Checking the card continuously. It's not sleeping at all for 15secs. What is wrong with this code ?
You're calling chkRequestTask recursively using Task.Factory.StartNew which you don't need at all.
It's not clear why you need to poll the status, better idea is to check any event or callback or WaitHandle provided by the card API you're talking about. That should keep you away from the pain.
If at all you believe polling is the only option you've left with, you can do it as follows.
static async Task ChkRequestTask(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Checking the card");
bool status = PollTheCardForStatus();
if(!status)
break;
await Task.Delay(15 * 1000, token);//Adjust the delay as you wish
}
}
Else where in code, if possible await the call, If not then attach a continuation or use blocking task.Wait.
await ChkRequestTask(token);
This method doesn't need to return bool as you're returning from the method only when it is false, it is safe to assume the status is false when the Task returned from ChkRequestTask completes, which means poll returned false or the CancellationToken is cancelled, in which case you'll get TaskCanceledException
This is how I have done this. It seems to be working properly. As it's a background thread it would exit when the application exits. Could someone advise If this is the right way to do it.
private void Form1_Load(object sender, EventArgs e)
{
m_dev = DASK.Register_Card(DASK.PCI_7250, 0);
if (m_dev < 0)
{
MessageBox.Show("Register_Card error!");
}
FunctionToCall();
}
private void FunctionToCall()
{
short ret;
uint int_value;
var thread = new Thread(() =>
{
while (true)
{
ret = DASK.DI_ReadPort((ushort)m_dev, 0, out int_value);
if (ret < 0)
{
MessageBox.Show("D2K_DI_ReadPort error!");
return;
}
if (int_value > 0)
{
textBox2.Invoke(new UpdateText(DisplayText), Convert.ToInt32(int_value));
}
Thread.Sleep(500);
}
});
thread.Start();
thread.IsBackground = true;
}
private void DisplayText(int i)
{
textBox2.Text = i.ToString();
}
I am starting a thread and updating form element but following code is making form unresposive please suggest what I am doing wrong here.
private void StartTimer()
{
Thread t = new Thread(Updates);
t.IsBackground = true;
t.Start();
}
private void Updates()
{
try
{
while (true)
{
this.Invoke((MethodInvoker)delegate
{
lblTotalImages.Text = AppHelper.GetTotalCount(textBox1.Text).ToString();
if (sitename != null)
{
lblTotalPosted.Text = AppHelper.GetPostedCount(sitename).ToString();
// Application.DoEvents();
}
});
Thread.Sleep(1000);
}
}
catch (Exception ex)
{
}
}
Edit-------
Thanks #Servy for introducing Task Parallel Library i have never used it here is solution i come up with i used timer and task factory
ya for .net 4 without library extension and vs 2010 i have this working perfectly
enter code here private void timer1_Tick(object sender, EventArgs e)
{
Task.Factory.StartNew(() => AppHelper.GetTotalCount(textBox1.Text)).ContinueWith(t => lblTotalImages.Text = t.Result.ToString(), TaskScheduler.FromCurrentSynchronizationContext());
if (sitename != null)
{
Task.Factory.StartNew(() => AppHelper.GetPostedCount(sitename)).ContinueWith(t => lblTotalPosted.Text = t.Result.ToString(), TaskScheduler.FromCurrentSynchronizationContext());
}
}
You are doing some sort of long running non-UI operation inside of the call to Invoke, which means it's being done in the UI thread. You should strive to only be updating the UI in the UI thread; ensure that any long running non-UI code is outside of any invocations to the UI thread.
The use of await also allows this solution to be written in a much simpler and more effective manor:
public async void StartTimer()
{
while (true)
{
string text = textBox1.Text;
lblTotalImages.Text = await Task.Run(() =>
AppHelper.GetTotalCount(text).ToString());
if (sitename != null)
{
lblTotalPosted.Text = await Task.Run(() =>
AppHelper.GetPostedCount(sitename).ToString());
}
await Task.Delay(TimeSpan.FromSeconds(1));
}
}