.NET 4.7.2 => c-sharp => UI async/await - multithreading

I'm trying to load a content of a text file into text box asynchronously.
Therefore my goal is to not block the UI-Thread.
To check that, the code gives feedback with Console.Writeline("..."+Thread.CurrentThread.ManagedThreadId)
My first intention shows the code below, which will give me Thread 1 in both Console.WriteLines().
That means UI-Thread is blocked, right?
private async void cmdProgrammLaden_Click(object sender, EventArgs e)
{
Console.WriteLine("Button-Thread-ID: " + Thread.CurrentThread.ManagedThreadId);
this.txtSendData.Text = await DncProgrammLadenAsync();
}
async Task<string> DncProgrammLadenAsync()
{
string path = String.Empty;
string content = String.Empty;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = #"c:\Test\";
openFileDialog.Filter = "Textfile (*.txt)|*.txt";
openFileDialog.FilterIndex = 0;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
path = openFileDialog.FileName;
var filestream = openFileDialog.OpenFile();
using (StreamReader reader = new StreamReader(filestream))
{
content = await reader.ReadToEndAsync();
Console.WriteLine("Task-Thread-ID: " + Thread.CurrentThread.ManagedThreadId);
//Thread.Sleep(3000);
}
}
return content;
}
}
Then I tried to rework this code like *(short version)*
Task.Run(() =>
{
string content = String.Empty;
using (StreamReader reader = new StreamReader(#"c:\Test\File C.txt"))
{
content = reader.ReadToEnd();
Console.WriteLine("Task-Thread-ID: " + Thread.CurrentThread.ManagedThreadId);
}
});
Then I get different Thread IDs.
But this seems not right to me.
What do I not understand correctly?

That means UI-Thread is blocked, right?
No. By default, await captures the current context and resumes executing the async method in that context when that await completes. The UI thread is not blocked during the await, but the code resumes executing on the UI thread after the await.
However, file streams are tricky. They are only asynchronous if opened with an asynchronous flag, and I have no idea if OpenFile passes that flag. Asynchronous APIs called on a synchronous file handle just run the operation on a thread pool thread (I think).

Related

Call delegate in the main thread

Let's say there is a library that handles events asynchronously, e.g. UDP broadcasting. I would like to be able to pass a delegate to this library and make sure that delegate is executed in the thread where it was defined.
public void Run(Action<string> action)
{
var PORT = 10000;
var udpClient = new UdpClient();
// Start listener
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, PORT));
var endpoint = new IPEndPoint(0, 0);
//var scheduler = TaskScheduler.Current; // No exception but executed in another thread
var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); // Raises exception
Task.Factory.StartNew(() =>
{
while (true)
{
action(Encoding.UTF8.GetString(udpClient.Receive(ref endpoint)));
}
}, CancellationToken.None, TaskCreationOptions.None, scheduler);
// Start sender
var data = Encoding.UTF8.GetBytes("PING");
udpClient.Send(data, data.Length, "255.255.255.255", PORT);
var aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimer);
aTimer.Interval = 1000;
aTimer.Enabled = true;
void OnTimer(object source, ElapsedEventArgs e)
{
var data = Encoding.UTF8.GetBytes("PONG");
udpClient.Send(data, data.Length, "255.255.255.255", PORT);
}
}
Then I check in which thread my delegate was executed.
Console.WriteLine("Start on Thread : " + Thread.CurrentThread.ManagedThreadId);
new Beacon().Run(response =>
{
Console.WriteLine("Continue on Thread : " + Thread.CurrentThread.ManagedThreadId);
});
If I use TaskScheduler.Current nothing changes, the delegate is executed in another thread. If I use TaskScheduler.FromCurrentSynchronizationContext, I get an exception below.
The current SynchronizationContext may not be used as a TaskScheduler
Questions
Why I can't use synchronization context in .NET core or how to use it properly?
Is there a better way of marshaling delegate call to the main thread?
Are BackgroundWorker and EventLoopScheduler obsolete now?

C# how to call async await in a for loop

I am developing a quartz.net job which runs every 1 hour. It executes the following method. I am calling a webapi inside a for loop. I want to make sure i return from the GetChangedScripts() method only after all thread is complete? How to do this or have i done it right?
Job
public void Execute(IJobExecutionContext context)
{
try
{
var scripts = _scriptService.GetScripts().GetAwaiter().GetResult();
}
catch (Exception ex)
{
_logProvider.Error("Error while executing Script Changed Notification job : " + ex);
}
}
Service method:
public async Task<IEnumerable<ChangedScriptsByChannel>> GetScripts()
{
var result = new List<ChangedScriptsByChannel>();
var currentTime = _systemClock.CurrentTime;
var channelsToProcess = _lastRunReader.GetChannelsToProcess().ToList();
if (!channelsToProcess.Any()) return result;
foreach (var channel in channelsToProcess)
{
var changedScripts = await _scriptRepository.GetChangedScriptAsync(queryString);
if (changedScriptsList.Any())
{
result.Add(new ChangedScriptsByChannel()
{
ChannelCode = channel.ChannelCode,
ChangedScripts = changedScriptsList
});
}
}
return result;
}
As of 8 days ago there was a formal announcement from the Quartz.NET team stating that the latest version, 3.0 Alpha 1 has full support for async and await. I would suggest upgrading to that if at all possible. This would help your approach in that you'd not have to do the .GetAwaiter().GetResult() -- which is typically a code smell.
How can I use await in a for loop?
Did you mean a foreach loop, if so you're already doing that. If not the change isn't anything earth-shattering.
for (int i = 0; i < channelsToProcess.Count; ++ i)
{
var changedScripts =
await _scriptRepository.GetChangedScriptAsync(queryString);
if (changedScriptsList.Any())
{
var channel = channelsToProcess[i];
result.Add(new ChangedScriptsByChannel()
{
ChannelCode = channel.ChannelCode,
ChangedScripts = changedScriptsList
});
}
}
Doing these in either a for or foreach loop though is doing so in a serialized fashion. Another approach would be to use Linq and .Select to map out the desired tasks -- and then utilize Task.WhenAll.

Npgsql LISTEN Thread Crashing Server

I have a long running PostgreSQL function. For simplicity, something like this:
CREATE FUNCTION pg_function()
RETURNS void
AS
$$
BEGIN
PERFORM pg_notify('channel1', 'pg_function() started.');
PERFORM pg_sleep(5);------PERFORM task1();-----------------------------------------
PERFORM pg_notify('channel1', 'Task1 payload.');
PERFORM pg_sleep(5);------PERFORM task2();-----------------------------------------
PERFORM pg_notify('channel1', 'Task2 payload.');
PERFORM pg_sleep(5);------PERFORM task3();-----------------------------------------
PERFORM pg_notify('channel1', 'Task3 payload.');
PERFORM pg_notify('channel1', 'pg_function() completed.');
END;
$$
LANGUAGE "plpgsql";
On C#, I have:
public bool listening;
public void PgFunction()
{
this.listening = true;
ThreadStart listenerStart = delegate
{
using (NpgsqlConnection connection = new NpgsqlConnection(this.connectionString))
{
connection.Open();
connection.Notification += Listen;
using (NpgsqlCommand listenChannel1 = new NpgsqlCommand("LISTEN channel1;", connection))
{
listenChannel1.ExecuteNonQuery();
}
while (this.listening)
{
using (NpgsqlCommand pollingCommand = new NpgsqlCommand("SELECT 0;", connection))
{
pollingCommand.ExecuteNonQuery();
}
Thread.Sleep(5000);
}
}
};
Thread listenerThread = new Thread(listenerStart) { IsBackground = false };
listenerThread.Start();
ThreadStart pgFunctionThreadStart = () => ExecuteNonQuery(new NpgsqlCommand("SELECT pg_function();"));
pgFunctionThreadStart += () =>
{
Thread.Sleep(5000);
this.listening = false;
};
Thread pgFunctionThread = new Thread(pgFunctionThreadStart) { IsBackground = true };
pgFunctionThread.Start();
}
private void Listen(object sender, NpgsqlNotificationEventArgs e)
{
string payload = e.AdditionalInformation;
//SignalR stuff here
}
When I run the program debugging, this code works okay. But when it is tested on IIS server or browsed with Visual Studio 2013 integrated IIS, the application crashes. Since I have very little knowledge of tasks and threads in C#, I would like to know what I am doing wrong here? Please advise.
Edit
Upon debugging it again, I came with a NpgsqlException, which happens to happen once in a while:
Additional information: Cannot write to a BufferedStream while the read buffer is not empty if
the underlying stream is not seekable. Ensure that the stream underlying this BufferedStream
can seek or avoid interleaving read and write operations on this BufferedStream.

.NET 4.0 BackgroundWorker class and unusual behavior

I am running into some strange behavior in the backgroundworker class that leads me to believe that I don't fully understand how it works. I assumed that the following code sections were more or less equal except for some extra features that the BackgroundWorker implements (like progress reporting, etc.):
section 1:
void StartSeparateThread(){
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//Execute some code asynchronous to the thread that owns the function
//StartSeparateThread() but synchronous to itself.
var SendCommand = "SomeCommandToSend";
var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
var SecondsToWait = 30;
//this calls a function that sends the command over the NetworkStream and waits
//for various responses.
var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
}
Section 2:
void StartSeparateThread(){
Thread pollThread = new Thread(new ThreadStart(DoStuff));
pollThread.Start();
}
void DoStuff(object sender, DoWorkEventArgs e)
{
//Execute some code asynchronous to the thread that owns the function
//StartSeparateThread() but synchronous to itself.
var SendCommand = "SomeCommandToSend";
var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
var SecondsToWait = 30;
//this calls a function that sends the command over the NetworkStream and waits
//for various responses.
var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
}
I was using Section 1 to run some code that sent a string over a networkstream and waited for a desired response string, capturing all output during that time. I wrote a function to do this that would return the networkstream output, the index of the the sent string, as well as the index of the desired response string. I was seeing some strange behavior with this so I changed the function to only return when both the send string and the output string were found, and that the index of the found string was greater than the index of the sent string. It would otherwise loop forever (just for testing). I would find that the function would indeed return but that the index of both strings were -1 and the output string was null or sometimes filled with the expected output of the previous call. If I were to make a guess about what was happening, it would be that external functions called from within the bw_DoWork() function are run asynchronously to the thread that owns the bw_DoWork() function. As a result, since my SendAndWaitFor() function was called multiple times in succession. the second call would be run before the first call finished, overwriting the results of the first call after they were returned but before they could be evaluated. This seems to make sense because the first call would always run correctly and successive calls would show the strange behavior described above but it seems counter intuitive to how the BackgroundWorker class should behave. Also If I were to break within the SendAndWaitFor function, things would behave properly. This again leads me to believe there is some multi-threading going on within the bwDoWork function itself.
When I change the code in the first section above to the code of the second section, things work entirely as expected. So, can anyone who understands the BackgroundWorker class explain what could be going on? Below are some related functions that may be relevant.
Thanks!
public Dictionary<string, string> SendAndWaitFor(string sendString, List<string> toWaitFor, int seconds)
{
var toReturn = new Dictionary<string, string>();
var data = new List<byte>();
var enc = new ASCIIEncoding();
var output = "";
var FoundString = "";
//wait for current buffer to clear
output = this.SynchronousRead();
while(!string.IsNullOrEmpty(output)){
output = SynchronousRead();
}
//output should be null at this point and the buffer should be clear.
//send the desired data
this.write(enc.GetBytes(sendString));
//look for all desired strings until timeout is reached
int sendIndex=-1;
int foundIndex = -1;
output += SynchronousRead();
for (DateTime start = DateTime.Now; DateTime.Now - start < new TimeSpan(0, 0, seconds); )
{
//wait for a short period to allow the buffer to fill with new data
Thread.Sleep(300);
//read the buffer and add it to the output
output += SynchronousRead();
foreach (var s in toWaitFor)
{
sendIndex = output.IndexOf(sendString);
foundIndex = output.LastIndexOf(s);
if (foundIndex>sendIndex)
{
toReturn["sendIndex"] = sendIndex.ToString();
toReturn["foundIndex"] = sendIndex.ToString();
toReturn["Output"] = output;
toReturn["FoundString"] = s;
return toReturn;
}
}
}
//Set this to loop infinitely while debuging to make sure the function was only
//returning above
while(true){
}
toReturn["sendIndex"]="";
toReturn["foundIndex"]="";
toReturn["Output"] =output;
toReturn["FoundString"] = "";
return toReturn;
}
public void write(byte[] toWrite)
{
var enc = new ASCIIEncoding();
var writeString = enc.GetString(toWrite);
var ns = connection.GetStream();
ns.Write(toWrite, 0, toWrite.Length);
}
public string SynchronousRead()
{
string toReturn = "";
ASCIIEncoding enc = new ASCIIEncoding();
var ns = connection.GetStream();
var sb = new StringBuilder();
while (ns.DataAvailable)
{
var buffer = new byte[4096];
var numberOfBytesRead = ns.Read(buffer, 0, buffer.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, numberOfBytesRead));
toReturn += sb.ToString();
}
return toReturn;
}
All data to be used by a background worker should be passed in through the DoWorkEventArgs and nothing should be pulled off of the class (or GUI interface).
In looking at your code I could not identify where the property(?) connnection was being created. My guess is that connection is created on a different thread, or may be pulling read information, maybe from a GUI(?) and either one of those could cause problems.
I suggest that you create the connection instance in the dowork event and not pull an existing one off of a different thread. Also verify that the data connection works with does not access any info off of a GUI, but its info is passed in as its made.
I discuss an issue with the Background worker on my blog C# WPF: Linq Fails in BackgroundWorker DoWork Event which might show you where the issue lies in your code.

Error Cross-thread operation not valid: Control 'CameraViewVS' accessed from a thread other than the thread it was created on. parallel.for

I have a timer to verify one condition every time and show pop up form only once if the condition is verified. I want to verify in parallel all instances, so i used parallel.for, but i have this error "Cross-thread operation not valid: Control 'CameraViewVS' accessed from a thread other than the thread it was created on." in line " frm.WindowState = FormWindowState.Normal;"
this is my code:
public void timer1_Tick(object source, EventArgs e)
{
Parallel.For(0, nbre, l =>
{
cameraInstanceList[l].Start();
if (cameraInstanceList[l].MoveDetection == true)
{
//show the the form S once
foreach (Form S in Application.OpenForms)
{
var frm = S as Formes.CameraViewVS;
if (frm != null && frm.IP == cameraInstanceList[l].adresse)
{
cameraInstanceList[l].MoveDetection = false;
frm.WindowState = FormWindowState.Normal;
frm.Activate();
return;
}
}
f1 = new Formes.CameraViewVS(cameraInstanceList[l],
adresseIPArray[l]);
f1.Show(this);
}
}
);
Most properties on WinForm object instances need to be accessed from the thread that they were created on. You can use the Control.InvokeRequired property to determine if you need to use the control (or form) Invoke method to execute the code on the UI thread.
It is also a good practise to create most WinForm controls on the main UI thread, and not on any thread pool threads. In WinForms applications, you can use the SynchronizationContext to ensure some code, such as creating a form, is called on the UI thread.
EDIT: changed so that the method doesn't return after movement detected.
public void timer1_Tick(object source, EventArgs e)
{
// assume this is being called on the UI thread, and save the thread synchronization context
var uiContext = SynchronizationContext.Current;
Parallel.For(0, nbre, l =>
{
while (true)
{
Thread.Sleep(250); // <--- sleep for 250 ms to avoid "busy" wait
cameraInstanceList[l].Start();
if (cameraInstanceList[l].MoveDetection == true)
{
// capture instances used in closures below
var cameraInstance = cameraInstanceList[l];
var ipAdresse = adresseIPArray[l];
//show the the form S once
foreach (Form S in Application.OpenForms)
{
var frm = S as Formes.CameraViewVS;
if (frm != null)
{
// create delegate to be invoked on form's UI thread.
var action = new Action(() =>
{
if (frm.IP == cameraInstance.adresse)
{
cameraInstance.MoveDetection = false;
frm.WindowState = FormWindowState.Normal;
frm.Activate();
}
};
if (frm.InvokeRequired)
frm.Invoke(action);
else
action();
continue; // <--- go back to the top of the while loop
// and wait for next detection
}
}
// create delegate to create new form on UI thread.
var createNewFormCallback = new SendOrPostCallback((o) =>
{
f1 = new Formes.CameraViewVS(cameraInstance, ipAdresse);
f1.Show(this);
};
// and invoke the delegate on the ui thread
uiContext.Send(createNewFormCallback, null);
}
}
}
);
}
Thomas is very close to right answer ,Because Every Control runs in a different thread .You should just write a code for context-switching of resources which is being used by Controls
Thread ..Don't worry you have a lot of facility for this in c sharp.Just use BeginInvoke and Invoke and i hope you would be able to resolve your problem.Write this in place of your old code block ..
var action = new Action(() =>
{
if (frm.IP == cameraInstance.adresse)
{
cameraInstance.MoveDetection = false;
frm.WindowState = FormWindowState.Normal;
frm.Activate();
}
};
if (frm.InvokeRequired)
frm.BeginInvoke(action);
else
frm.Invoke(action);

Resources