I'am using WPF with multi threading. Problem is during execution of the MAIN thread hangs, like its waiting for something or infinite loop in main UI. I don't know if there's any other approach when it comes to multi threading in WPF. See my code below:
Thread myNewThread1 = new Thread(() => ping(IP1, img_m));
Thread myNewThread2 = new Thread(() => ping(IP2, img_h));
Thread myNewThread3 = new Thread(() => ping(IP3, img_c));
Thread myNewThread4 = new Thread(() => ping(IP4, img_b));
Thread myNewThread5 = new Thread(() => ping(IP5, img_e));
myNewThread1.Start();
myNewThread2.Start();
myNewThread3.Start();
myNewThread4.Start();
myNewThread5.Start();
private void ping(string IP, Image img)
{
this.Dispatcher.Invoke(() =>
{
Ping p = new Ping();
var r = p.Send(IP, 1000, new byte[5]);
if (r.Status == IPStatus.Success)
{
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("subonline.gif", UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
}
else
{
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri("suboffline.gif", UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
}
});
Thread.Sleep(500);
ping(IP, img);
}
Your main thread hangs because of Dispatcher.Invoke wrong usage, it should be used for UI logic only, so you should move out the Ping-oriented logic out of it.
Do not use the BackgroundWorker for this, it's an obsolete and heavy construction you don't really need. Also, do not a thread for pinging, this is wrong approach, and that's why:
Pinging operation is network-related, and the thread you're using for wait the remote server to response simply wastes the system resources, as it does absolutely nothing except wait. So you should switch to asynchronous approach for this.
You either should subscribe to Ping.PingCompleted event and call the SendAsync method, like this:
private void ping(string IP, MediaTypeNames.Image img)
{
Ping p = new Ping();
PingReply r;
// lambda delegate here, may use event handler instead
p.PingCompleted += (sender, args) => { PingCompleted(args, img); };
r = p.SendAsync(IP, 1000, new byte[5], null);
}
private void PingCompleted(PingCompletedEventArgs args, MediaTypeNames.Image img)
{
this.Dispatcher.Invoke(() =>
{
string imageAddress;
if (args.Reply.Status == IPStatus.Success)
{
imageAddress = "subonline.gif";
}
else
{
imageAddress = "suboffline.gif";
}
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(imageAddress, UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
});
}
or you should use async/await feature which was introduced exactly for such cases (code partially from this answer):
// async event handler
private async void btn_Click(object sender, EventArgs e)
{
// async call to all the ips
var results = await PingAsync(new List<string> { IP1, IP2, IP3, IP4, IP5 });
// here we do not need the Dispatcher as await will restore UI context for you
PingCompleted(results[0], img_m);
// etc
}
private void PingCompleted(PingReply r, MediaTypeNames.Image img)
{
string imageAddress;
if (r.Status == IPStatus.Success)
{
imageAddress = "subonline.gif";
}
else
{
imageAddress = "suboffline.gif";
}
var image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(imageAddress, UriKind.Relative);
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageBehavior.SetAnimatedSource(img, image);
}
// helper method
private async Task<PingReply[]> PingAsync(List<string> theListOfIPs)
{
Ping pingSender = new Ping();
var tasks = theListOfIPs.Select(ip => pingSender.SendPingAsync(ip, 1000, new byte[5]));
return await Task.WhenAll(tasks);
}
Do not include the call to p.Send method in the dispatcher - currently only the Thread.Sleep(500) is done in the background and everything else is done in the UI thread.
Also, I believe that because of the Thread.Sleep the function never gives the UI thread a chance to work so the pinging never occurs. Replace the use of Thread.Sleep with a Thread.Timer object.
Related
First of all, sorry because I am so new at C# and I decided to make this question because I have been choked in this for hours.
I have an GUI that works with Google Cloud Speech services and make a Speech-to-Text operation. I share with you the whole method that runs when a button is clicked:
private async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
GoogleCredential googleCredential;
using (Stream m = new FileStream(#"..\..\credentials.json", FileMode.Open))
googleCredential = GoogleCredential.FromStream(m);
var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
googleCredential.ToChannelCredentials());
var speech = SpeechClient.Create(channel);
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 48000,
LanguageCode = "es-ES",
},
InterimResults = true,
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
//Textbox1.Text = alternative.Transcript;
}
}
}
});
waveIn.StartRecording();
Console.WriteLine("Speak now.");
Result_Tone.Text = "Speak now:\n\n";
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
My problem is that I want to update the content of the Textbox1control but it doesn´t work. It writes perfectly the output into the console with the line Console.WriteLine(alternative.Transcript); but not into my textbox.
If someone could help I would appreciate so much his help.
The problem is that you're using Task.Run, which means your code will be running on a thread-pool thread.
Instead of calling Task.Run(), just move that code into a separate async method:
async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
while (await responses.MoveNext(default(CancellationToken)))
{
foreach (var result in responses.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}
}
Then call that method directly (without Task.Run) from code that's already on the UI thread (e.g. an event handler).
The async machinery will make sure that after the await expression, you're back on the UI thread (the same synchronization context). So the assignment to the Text property will occur on the UI thread, and all should be well.
For example:
// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
var stream = client.StreamingRecognize();
// Send the initial config request
await stream.WriteAsync(...);
// Presumably you want to send audio data...
StartSendingAudioData(stream);
await DisplayResponses(stream.ResponseStream);
}
Tasks run on seperate threads, so you must Invoke an action that will be performed on the control's thread
Textbox1.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
Edit: For WPF, I believe the equivalent is
Textbox1.Dispatcher.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
have you tried using Dispatcher.InvokeASync()?
await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}});
I'm calling from the main method:
public MainPage()
{
Text_to_Speech.changetospeech("Welcome to Nepal!", newmedia).Wait();
mytxtblck.Text="Hello from Nepal!"
}
What I really want to do is Wait until "Welcome to Nepal" is being spoken and then write "Hello" in mytextblck.
I've gone to several threads and worked, but nothing could make it work.
public async static Task changetospeech(string text, MediaElement mediaa)
{
var synth = new SpeechSynthesizer();
var voices = SpeechSynthesizer.AllVoices;
synth.Voice = voices.First(x => x.Gender == VoiceGender.Female );
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
MediaElement media = mediaa;
media.SetSource(stream,stream.ContentType);
media.Play();
}
It sounds like what you really want is to trigger the text change when the MediaEnded event is fired.
You could do that within your ChangeToSpeech method, although it'll be a little ugly:
public async static Task ChangeToSpeech(string text, MediaElement media)
{
var synth = new SpeechSynthesizer();
var voices = SpeechSynthesizer.AllVoices;
synth.Voice = voices.First(x => x.Gender == VoiceGender.Female);
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
var tcs = new TaskCompletionSource<int>();
RoutedEventHandler handler = delegate { tcs.TrySetResult(10); };
media.MediaEnded += handler;
try
{
media.SetSource(stream, stream.ContentType);
media.Play();
// Asynchronously wait for the event to fire
await tcs.Task;
}
finally
{
media.MediaEnded -= handler;
}
}
I'm getting InvalidOperationException. This code is running in a thread. I've delegate it to the UI thread to handle the UI. Is the problem occuring because of my resources?
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/LiarDice;component/" + imagePath);
logo.EndInit();
currentGuessDice.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate()
{
currentGuessDice.Source = logo;
}));
I changed my code slightly a little and now the error changes to:
Part URI cannot end with a forward slash.
New Code:
currentGuessDice.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate()
{
BitmapImage logo = new BitmapImage();
logo.BeginInit();
logo.UriSource = new Uri("pack://application:,,,/LiarDice;component/" + imagePath);
logo.EndInit();
currentGuessDice.Source = logo;
}));
Besides that your imagePath variable my be null, the problem in your original code is that you create a BitmapImage in a thread other than the UI thread, and then use it in the UI thread.
BitmapImage is a DispatcherObject, which means that is has thread affinity. Since it is also a Freezable, you can freeze it in order to make it accessible to threads other than the one in which it was created:
var logo = new BitmapImage(
new Uri("pack://application:,,,/LiarDice;component/" + imagePath));
logo.Freeze(); // here
currentGuessDice.Dispatcher.Invoke(new Action(() => currentGuessDice.Source = logo));
I am trying to get the thumbnails for files in a directory asynchronously. All files except PDFs seem to work async.
if (System.IO.File.Exists(filePath))
{
var task = await Task.Factory.StartNew(async () =>
{
using(ShellFile shellFile = ShellFile.FromFilePath(filePath))
{
ImageSource source = shellFile.Thumbnail.MediumBitmapSource;
source.Freeze();
return source;
}
});
image.Dispatcher.Invoke(() => image.Source = task.Result);
}
All other files return correctly. However, if I call all of this code a second time if image.source == null then it works fine.
edit
My working code after Hans Passant's answer
var thread = new Thread(() =>
{
using(ShellFile shellFile = ShellFile.FromFilePath(filePath))
{
ImageSource source = shellFile.Thumbnail.MediumBitmapSource;
source.Freeze();
}
image.Dispatcher.Invoke(() => image.Source = source);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Thanks!
I'm getting a strange AccessViolationException in a WPF application. Since there is no user code on the stack I'm not really sure how to go about troubleshooting this. It also only happens every 2-3 days which adds to the troubleshooting complexity.
at MS.Win32.PresentationCore.UnsafeNativeMethods+MILUnknown.Release(IntPtr)
at MS.Win32.PresentationCore.UnsafeNativeMethods+MILUnknown.ReleaseInterface(IntPtr ByRef)
at System.Windows.Media.SafeMILHandle.ReleaseHandle()
at System.Windows.Media.Imaging.BitmapSourceSafeMILHandle.ReleaseHandle()
at System.Runtime.InteropServices.SafeHandle.InternalFinalize()
at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean)
at System.Runtime.InteropServices.SafeHandle.Finalize()
My image manipulation code (slightly simplified)
private int timerCount;
private void TimerProc() // called from a timer obviously
{
if(Interlocked.Increment(ref timerCount) !=0)
{
Interlocked.Decrement(ref timerCount);
return;
}
try
{
byte[] img = FetchImageFromExternalSource(); //returns a jpeg image
this.Image = LoadImage(new MemoryStream(img));
}
finally
{
Interlocked.Decrement(ref timerCount);
}
}
private BitmapImage LoadImage(Stream inputStream)
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = inputStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
private BitmapImage image;
public BitmapImage Image
{
get
{
return image;
}
set
{
if (image!= value)
{
this.dispatcher.BeginInvoke(new Action(()=>
{
image = value;
PropertyChanged(this,new PropertyChangedEventArgs("Image"));
}),null);
}
}
}
Any advice on if I'm hitting a known bug (.NET 4) or if there is some problem with the code that I've missed would be appreciated.
System.Threading.Timer invokes the callback on a background thread, not on the UI thread, which the BitmapImage class is probably expecting. Try using DispatcherTimer.