I'm trying to replace my ProgressBar to a Progress Dialog using Mahapps.
So I started writing this:
private void btnClick(object sender, RoutedEventArgs e)
{
ConfRelais();
}
public async void ConfRelais()
{
var controller = await this.ShowProgressAsync("hey", "hoy");
controller.Maximum = 128;
while (flag == 0)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok") { controller.SetMessage("Done Process");
flag = 1; }
else { controller.SetProgress(Int32.Parse(data)); }
}
await controller.CloseAsync();
}
But the progress dialog only displays when it's over.. As I'm still a beginner in c# maybe I'm missing some importants points to setup that kind of function.
You should execute the loop on a background thread:
public async void ConfRelais()
{
var controller = await this.ShowProgressAsync("hey", "hoy");
controller.Maximum = 128;
await Task.Run(() =>
{
while (flag == 0)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok")
{
controller.SetMessage("Done Process");
flag = 1;
}
else { controller.SetProgress(Int32.Parse(data)); }
}
});
await controller.CloseAsync();
}
A single thread cannot both update the UI and execute your loop simultaneously.
You also don't really need a flag. You could just break out of the loop when you receive "ok":
while (true)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok")
{
controller.SetMessage("Done Process");
break;
}
else { controller.SetProgress(Int32.Parse(data)); }
}
Related
I'm using Two await function in my program, and first one is working flawlessly but on encountering second await, my program comes out of function without executing that awaitable function and any line after that.
Not giving error and not even crashing.
Tried "wait()", "Running on main thread", "Task Run", "Threading Sleep"..
here a snippet of the code along
private async void Btn_Clicked(object sender, EventArgs e)
{
//this statement works perfectly fine
var status = await CrossPermissions.Current.RequestPermissionAsync<LocationPermission>();
//on debugging i found out it return out of function at this point without executing
var check = await CrossPermissions.Current.RequestPermissionAsync<CameraPermission>();
//None of the code is executed
Console.WriteLine("Granted");
Console.WriteLine("Granted");
}
You try to use the below method :
private async void Btn_Clicked(object sender, EventArgs e)
{
await GetPermissions();
}
public static async Task<bool> GetPermissions()
{
bool permissionsGranted = true;
var permissionsStartList = new List<Permission>()
{
Permission.Location,
Permission.Camera,
};
var permissionsNeededList = new List<Permission>();
try
{
foreach (var permission in permissionsStartList)
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
if (status != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
{
permissionsNeededList.Add(permission);
}
}
}
catch (Exception ex)
{
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(permissionsNeededList.ToArray());
try
{
foreach (var permission in permissionsNeededList)
{
var status = Plugin.Permissions.Abstractions.PermissionStatus.Unknown;
//Best practice to always check that the key exists
if (results.ContainsKey(permission))
status = results[permission];
if (status == Plugin.Permissions.Abstractions.PermissionStatus.Granted || status == Plugin.Permissions.Abstractions.PermissionStatus.Unknown)
{
permissionsGranted = true;
}
else
{
permissionsGranted = false;
break;
}
}
}
catch (Exception ex)
{
}
return permissionsGranted;
}
First of all I am a complete begineer in C# and literally don't know how to use async/await/cancellation/Tasks, I come from a embedded systems background with basic-moderate C/C++ knowledge. So i just get the idea what those async functions do here. the async task codes are copied from samples from microsoft, they simply keep on reading the serial port for new bytes. I made some animations for the UI through Blend, and that's just it nothing more fancy.
This app runs on a raspberry with IOT core, and its sole job is to provide a UI for user to control a microcontroller with plain text instructions send through serial port. All these buttons simply send some strings to serial port which my microcontroller reads and acts accordingly.
Problem 1: I modified a line in the ReadAsync function uint ReadBufferLength = 1; the value was 128 in sample code, my microcontroller sends a complete instruction or data ending with "\r\n". This meant it will keep on reading bytes in serial that come from the microcontroller until the buffer is full and call the ReadAsync function again. since it was reading until the buffer (128) was full, multiple instructions come and fill the buffer. So i had no way to figure out what is the instruction in the buffer. So my work around here was to change the buffer to 1 and append a string with new bytes from serial port until it finds Environment.NewLine and then build logic based on the complete instruction in the string.
Question 1: How do I modify the code to not change the uint ReadBufferLength = 1; and make the ReadAsync function store my serial incoming data until it finds \r\n ?
MAJOR Problem 2: As soon as I enter Manual/Auto mode by clicking the front panel button, my microcontroller starts sending temperature sensor value through serial port as fast as possible without any delay between instructions. I know the raspberry pi (win10 iot core) is fast enough to not miss any instruction or the physical serial port buffer of the PI getting overfilled. It works fine, there is no problem at this point in the app, no crash or slowdowns. But overtime I can see that the app is consuming more and more RAM. the behavior is same in PC if I build it in x86 mode. after running it for 1 hour it will consume over 120 megabytes whereas it starts at 30-50 megabytes initially.
To recreate the problem, please change string qFilter = SerialDevice.GetDeviceSelector("UART0"); UART0 to appropriate com port in windows. otherwise the app will not run. Probably a Virtual COM port needs to be installed or a usb to ttl module might be required.
Problem 3: Cannot build the app in release mode using the option "Compile with .NET Native tool chain", without this it runs fine in the raspberry.
Internal compiler error: MCG0024:UnresolvableTypeReference Unresolvable type reference 'System.Runtime.InteropServices.WindowsRuntime.IRestrictedErrorInfo' in 'Assembly(Name=System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)' found. Please check the references in your build system. A reference is either missing or an assembly is missing an expected type. hvac_version_4
github project file
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
using System.Threading;
using Windows.Devices.Enumeration;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
using Microsoft.Toolkit.Uwp.UI.Animations.Behaviors;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace hvac_version_2
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
SolidColorBrush greenBrush = new SolidColorBrush(Colors.Green);
SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
SolidColorBrush greyBrush = new SolidColorBrush(Colors.DarkSlateGray);
//private bool updatingValues;
private CancellationTokenSource ReadCancellationTokenSource;
private SerialDevice serialPort = null;
DataWriter dataWriteObject = null;
DataReader dataReaderObject = null;
public string strFromPort;
private string final_string;
private int compressorAkey=0;
private int compressorBkey=0;
private int pump_entry = 0;
private int compAentry=0;
private int compBentry=0;
private int fan1entry = 0;
private int fan2entry = 0;
private double Blur_amount = 0;
public MainPage()
{
this.InitializeComponent();
stop_button.IsEnabled = false;
set_temperature.IsEnabled = false;
set_switching_time.IsEnabled = false;
auto_mode.IsEnabled = false;
manual_mode.IsEnabled = false;
compressorA_button.IsEnabled = false;
compressorB_button.IsEnabled = false;
fanA_button.IsEnabled = false;
fanB_button.IsEnabled = false;
init.IsEnabled = false;
init_timing.IsEnabled = false;
pump_button.IsEnabled = false;
load_animation.Begin();
}
private async void stop_button_Click(object sender, RoutedEventArgs e)
{
//do something
start_animation.Stop();
if (serialPort == null) return;
await sendToPort("end");
set_temperature.IsEnabled = false;
set_switching_time.IsEnabled = false;
manual_mode.IsEnabled = true;
auto_mode.IsEnabled = true;
stop_button.IsEnabled = false;
compressorA_button.IsEnabled = false;
compressorB_button.IsEnabled = false;
fanA_button.IsEnabled = false;
fanB_button.IsEnabled = false;
init.IsEnabled = false;
init_timing.IsEnabled = false;
pump_button.IsEnabled = false;
start_button.IsEnabled = true;
pump_entry = 0;
compAentry = 0;
compBentry = 0;
fan1entry = 0;
fan2entry = 0;
stop_animation.Begin();
start_button.Width = 162;
GC.Collect();
}
private async void set_temperature_changed(object sender, EventArgs e)
{
//System.Diagnostics.Debug.WriteLine("entered the async void set_temp.............");
}
private async void set_switching_time_value_changed(object sender, EventArgs e)
{
//System.Diagnostics.Debug.WriteLine("entered the async void switching time.............");
}
private async void manual_mode_Click(object sender, RoutedEventArgs e)
{
if (serialPort == null) return;
await sendToPort("manual");
auto_mode.IsEnabled = false;
manual_mode.IsEnabled = false;
stop_button.IsEnabled = true;
set_temperature.IsEnabled = true;
set_switching_time.IsEnabled = true;
pump_button.IsEnabled = true;
fanA_button.IsEnabled = true;
fanB_button.IsEnabled = true;
compressorA_button.IsEnabled = true;
compressorB_button.IsEnabled = true;
init.IsEnabled = true;
init_timing.IsEnabled = true;
}
private async void auto_mode_Click(object sender, RoutedEventArgs e)
{
set_switching_time.IsEnabled = true;
set_temperature.IsEnabled = true;
init.IsEnabled = true;
stop_button.IsEnabled = true;
init_timing.IsEnabled = true;
if (serialPort == null) return;
await sendToPort("auto");
}
private async void start_button_Click(object sender, RoutedEventArgs e)
{
stop_animation.Stop();
await sendToPort("ready");
auto_mode.IsEnabled = true;
manual_mode.IsEnabled = true;
start_button.IsEnabled = false;
start_animation.Begin();
System.Diagnostics.Debug.WriteLine("sending ready command");
}
private async void compressorA_button_Click(object sender, RoutedEventArgs e)
{
if (compAentry == 0)
{
if (serialPort == null) return;
await sendToPort("compressorAON"); //sending key
compAentry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("compressorAoff"); //sending key
compAentry = 0;
}
}
private async void compressorB_button_Click(object sender, RoutedEventArgs e)
{
if (compBentry == 0)
{
if (serialPort == null) return;
await sendToPort("compressorBON"); //sending key
compBentry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("compressorBoff"); //sending key
compBentry = 0;
}
}
private async void fanA_button_Click(object sender, RoutedEventArgs e)
{
if (fan1entry == 0)
{
if (serialPort == null) return;
await sendToPort("fan1on");
fan1entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("fan1off");
fan1entry = 0;
}
}
private async void fanB_button_Click(object sender, RoutedEventArgs e)
{
if (fan2entry == 0)
{
if (serialPort == null) return;
await sendToPort("fan2on");
fan2entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("fan2off");
fan2entry = 0;
}
}
private async void pump_button_Click(object sender, RoutedEventArgs e)
{
if (pump_entry == 0)
{
if (serialPort == null) return;
await sendToPort("pumpon");
pump_entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("pumpoff");
pump_entry = 0;
}
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
string qFilter = SerialDevice.GetDeviceSelector("UART0");
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(qFilter);
if (devices.Any())
{
string deviceId = devices.First().Id;
await OpenPort(deviceId);
}
ReadCancellationTokenSource = new CancellationTokenSource();
while (true)
{
//System.Diagnostics.Debug.WriteLine("program came before await listen");
await Listen();
}
}
private async Task OpenPort(string deviceId)
{
serialPort = await SerialDevice.FromIdAsync(deviceId);
if (serialPort != null)
{
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(50);
serialPort.BaudRate = 115200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
//Console.WriteLine("Serial port configured successfully");
//txtStatus.Text = "Serial port configured successfully";
}
}
private async Task Listen()
{
try
{
if (serialPort != null)
{
dataReaderObject = new DataReader(serialPort.InputStream);
await ReadAsync(ReadCancellationTokenSource.Token);
}
}
catch (Exception ex)
{
txtStatus.Text = ex.Message;
}
finally
{
if (dataReaderObject != null) // Cleanup once complete
{
dataReaderObject.DetachStream();
dataReaderObject = null;
}
}
}
private async Task ReadAsync(CancellationToken cancellationToken)
{
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 1; // only when this buffer would be full next code would be executed
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
//string debug = dataReaderObject.ReadString(bytesRead2);
//System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);
UInt32 bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
strFromPort = dataReaderObject.ReadString(bytesRead);
final_string = final_string + strFromPort;
//txtStatus2.Text = final_string;
if (final_string.Contains(Environment.NewLine))
{
string logicstring = final_string;
final_string = "";
//System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
if (logicstring.StartsWith("A"))
{
if(logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge1_pass = int.Parse(logicstring) * -1;
tempgauge1.Value = gauge1_pass;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge1_pass = int.Parse(logicstring);
tempgauge1.Value = gauge1_pass;
}
}
if (logicstring.StartsWith("B"))
{
if (logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge2_pass = int.Parse(logicstring) * -1;
tempgauge1.Value = gauge2_pass;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge2_pass = int.Parse(logicstring);
tempgauge2.Value = gauge2_pass;
}
}
if (logicstring.StartsWith("setting"))
{
if (logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_temp_setting = int.Parse(logicstring) * -1;
set_temperature.Value = previous_temp_setting;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_temp_setting = int.Parse(logicstring);
set_temperature.Value = previous_temp_setting;
}
}
if (logicstring.StartsWith("pumpon"))
{
pump_status_light.Fill = greenBrush;
Storyboard_Pump_Led.RepeatBehavior = RepeatBehavior.Forever;
Storyboard_Pump_Led.Begin();
}
if (logicstring.StartsWith("pumpoff"))
{
pump_status_light.Fill = redBrush;
Storyboard_Pump_Led.Stop();
}
if (logicstring.StartsWith("pumpfail"))
{
if (!FailPopup.IsOpen)
{
FailPopup.IsOpen = true;
}
}
if (logicstring.StartsWith("clear the popup"))
{
if (FailPopup.IsOpen)
{
FailPopup.IsOpen = false;
}
}
if (logicstring.StartsWith("switch_interval"))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_interval_setting = int.Parse(logicstring);
set_switching_time.Value = previous_interval_setting;
}
if (logicstring.StartsWith("timer"))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int remaining_time = int.Parse(logicstring);
remaining_time = remaining_time / 1000;
timer_box.Text = remaining_time.ToString();
}
if (logicstring.StartsWith("Free RAM = "))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int remaining_memory = int.Parse(logicstring);
free_memory.Text = remaining_memory.ToString();
}
if (logicstring.StartsWith("fan1on"))
{
fan1_status_light.Fill = greenBrush;
storyboard_fan1_led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_fan1_led.Begin();
}
if (logicstring.StartsWith("fan1off"))
{
fan1_status_light.Fill = redBrush;
storyboard_fan1_led.Stop();
}
if (logicstring.StartsWith("fan2on"))
{
fan2_status_light.Fill = greenBrush;
storyboard_fan_2.RepeatBehavior = RepeatBehavior.Forever;
storyboard_fan_2.Begin();
}
if (logicstring.StartsWith("fan2off"))
{
fan2_status_light.Fill = redBrush;
storyboard_fan_2.Stop();
}
if (logicstring.StartsWith("compressorAON")) //key
{
compressorA_status_light.Fill = greyBrush;
compressorAkey = 1;
}
if (logicstring.StartsWith("compressorAoff")) //key
{
compressorA_status_light.Fill = redBrush;
compressorAkey = 0;
}
if (logicstring.StartsWith("compressorBON")) //key
{
compressorB_status_light.Fill = greyBrush;
compressorBkey = 1;
}
if (logicstring.StartsWith("compressorBoff")) //key
{
compressorB_status_light.Fill = redBrush;
compressorBkey = 0;
}
if (logicstring.StartsWith("compressorA on")) // actually compressor on
{
compressorA_status_light.Fill = greenBrush;
storyboard_compA_Led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_compA_Led.Begin();
}
if (logicstring.StartsWith("compressorB on")) // actually compressor on
{
compressorB_status_light.Fill = greenBrush;
storyboard_compB_led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_compB_led.Begin();
}
if (logicstring.StartsWith("compressor off")) // actually compressor off
{
if (compressorBkey == 1)
{
compressorB_status_light.Fill = greyBrush;
storyboard_compB_led.Stop();
}
if (compressorAkey == 1)
{
compressorA_status_light.Fill = greyBrush;
storyboard_compA_Led.Stop();
}
if (compressorBkey == 0)
{
compressorB_status_light.Fill = redBrush;
storyboard_compB_led.Stop();
}
if (compressorAkey == 0)
{
compressorA_status_light.Fill = redBrush;
storyboard_compA_Led.Stop();
}
}
if (logicstring.StartsWith("compressorA off")) // actually compressor off
{
compressorA_status_light.Fill = redBrush;
storyboard_compA_Led.Stop();
}
if (logicstring.StartsWith("compressorB off")) // actually compressor off
{
compressorB_status_light.Fill = redBrush;
storyboard_compB_led.Stop();
}
}
}
}
private async Task WriteAsync(string text2write)
{
Task<UInt32> storeAsyncTask;
if (text2write.Length != 0)
{
dataWriteObject.WriteString(text2write);
storeAsyncTask = dataWriteObject.StoreAsync().AsTask(); // Create a task object
UInt32 bytesWritten = await storeAsyncTask; // Launch the task and wait
if (bytesWritten > 0)
{
//txtStatus.Text = bytesWritten + " bytes written at " + DateTime.Now.ToString(System.Globalization.CultureInfo.CurrentUICulture.DateTimeFormat.LongTimePattern);
}
}
else { }
}
private async Task sendToPort(string sometext)
{
try
{
if (serialPort != null)
{
dataWriteObject = new DataWriter(serialPort.OutputStream);
await WriteAsync(sometext);
}
else { }
}
catch (Exception ex)
{
txtStatus.Text = ex.Message;
}
finally
{
if (dataWriteObject != null) // Cleanup once complete
{
dataWriteObject.DetachStream();
dataWriteObject = null;
}
}
}
private void CancelReadTask()
{
if (ReadCancellationTokenSource != null)
{
if (!ReadCancellationTokenSource.IsCancellationRequested)
{
ReadCancellationTokenSource.Cancel();
}
}
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
CancelReadTask();
if (serialPort != null)
{
serialPort.Dispose();
}
serialPort = null;
}
private async void init_Click(object sender, RoutedEventArgs e)
{
double sendtempvalue = (double)set_temperature.Value;
if (serialPort == null) return;
await sendToPort("settemp," + sendtempvalue);
//System.Diagnostics.Debug.WriteLine("EEPROM " + sendtempvalue);
}
private async void init_timing_Click(object sender, RoutedEventArgs e)
{
double switching_time_minute = (double)set_switching_time.Value;
if (serialPort == null) return;
await sendToPort("switch_interval," + switching_time_minute);
//System.Diagnostics.Debug.WriteLine("EEPROM " + switching_time_minute);
}
/*
private void Page_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
}*/
}
}
Question 1: How do I modify the code to not change the uint
ReadBufferLength = 1; and make the ReadAsync function store my serial
incoming data until it finds \r\n ?
Please try to use following code. When using ReadBufferLength = 1, the transmission efficiency will be lower.
private async Task ReadAsync(CancellationToken cancellationToken)
{
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 128; // only when this buffer would be full next code would be executed
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
//string debug = dataReaderObject.ReadString(bytesRead2);
//System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);
UInt32 bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
strFromPort = dataReaderObject.ReadString(bytesRead);
final_string = final_string + strFromPort;
//txtStatus2.Text = final_string;
if (final_string.Contains(Environment.NewLine))
{
string logicstring = final_string;
final_string = final_string.Substring(final_string.IndexOf(Environment.NewLine) + 1);
//System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
}
}
}
Problem 2: after running it for 1 hour it will consume over 120
megabytes whereas it starts at 30-50 megabytes initially.
Please try to build the app in release but not using the .NET native toolchain.It's a known issue. Please see here.
Problem 3: Cannot build the app in release mode using the option
"Compile with .NET Native tool chain", without this it runs fine in
the raspberry.
Please remove the references runtime.win10-arm-aot.Microsoft.NETCore.UniversalWindowsPlatform and runtime.win10-arm.Microsoft.NETCore.UniversalWindowsPlatform in your project. The project will be compiled with .Net native tool chain successfully without the two libraries, since that these two libs depends on the native lib of System.Runtime.InteropServices.
I want to run my asynchronous methods GetPlayerCountryData() and GetPlayerTagsData() all together to save time instead of starting the next method only after the previous has completed. But I don't know how to do that.
https://jeremylindsayni.wordpress.com/2019/03/11/using-async-await-and-task-whenall-to-improve-the-overall-speed-of-your-c-code/
I have read this tutorial but I don't know how to use await Task.WhenAll() in my code.
In addition, I want to execute the line AllMethodsCompleted = true; after all my asynchronous methods have been completed successfully. Should I use await Task.WhenAll() in this situation?
How can I only set AllMethodsCompleted = true if all my asynchronous methods completed successfully? Is it possible to find out if (result.Error != null) or an exception occurred in one of the asynchronous methods before setting AllMethodsCompleted = true ?
string PlayerDeviceId = "";
private void RegisterGuestPlayFabAccount()
{
PlayerDeviceId = ReturnMobileID();
var requestIOS = new LoginWithIOSDeviceIDRequest { DeviceId = PlayerDeviceId, CreateAccount = true };
var loginTask = PlayFabClientAPI.LoginWithIOSDeviceIDAsync(requestIOS);
loginTask.ContinueWith(OnPlayFabRegisterGuestAccountComplete);
}
private void OnPlayFabRegisterGuestAccountComplete(Task<PlayFabResult<LoginResult>> task)
{
if (task.Result.Result != null)
{
PlayerAccountDetails();
}
if (task.Result.Error != null)
{
OnPlayFabError(task.Result.Error);
}
}
bool AllMethodsCompleted = false;
public async void PlayerAccountDetails()
{
await GetPlayerCountryData();
await GetPlayerTagsData();
AllMethodsCompleted = true;
}
private async Task GetPlayerTagsData()
{
var resultprofile = await PlayFabServerAPI.GetPlayerTagsAsync(new PlayFab.ServerModels.GetPlayerTagsRequest()
{
PlayFabId = PlayerPlayFabID
});
if (resultprofile.Error != null)
OnPlayFabError(result.Error);
else
{
if ((resultprofile.Result != null) && (resultprofile.Result.Tags.Count() > 0))
CurrentPlayerTag = resultprofile.Result.Tags[0].ToString();
}
}
private async Task GetPlayerCountryData()
{
var resultprofile = await PlayFabClientAPI.GetUserDataAsync(new PlayFab.ClientModels.GetUserDataRequest()
{
PlayFabId = PlayerPlayFabID,
Keys = null
});
if (resultprofile.Error != null)
OnPlayFabError(result.Error);
else
{
if (resultprofile.Result.Data == null || !resultprofile.Result.Data.ContainsKey("Country") || !resultprofile.Result.Data.ContainsKey("City"))
Console.WriteLine("No Country/City");
else
{
PlayerCountry = resultprofile.Result.Data["Country"].Value);
PlayerCity = resultprofile.Result.Data["City"].Value);
}
}
}
public async Task PlayerAccountDetails()
{
var playerCountryData = GetPlayerCountryData());
var playerTagsData = GetPlayerTagsData());
await Task.WhenAll(playerCountryData, playerTagsData);
AllMethodsCompleted = true;
}
Here is the method you are in query about and running the 2 methods in async waiting for each to finish then moving on. They just needed to be assigned to a task variable.
I have an app with a start button that calls a long running time function. In order
to add a Stop button I've added a thread for this function to avoid the UI freezes and be able to stop the processing in anytime.
The code without threading in average takes 12 minutes to complete the processing, but with threading in the way I have below
takes 4 times more. Below is shown the code for the start button where is called the function "LongRunningFunction" . The function
needs a string argument to work "LongRunningFunction(Somestring)".
I've tested with Task.Run and Task.Factory.StartNew but it happens the same with both methods.
Is there an alternative way to set a thread for my case that doesn't affect too much the performance?
public partial class Form1 : Form
{
CancellationTokenSource cts = new CancellationTokenSource(); // Create the token source.
public Form1()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
cts = new CancellationTokenSource();
Task.Run(()=> LongRunningFunction(Somestring, cts.Token), cts.Token);
//Task.Factory.StartNew(() => LongRunningFunction(Somestring, cts.Token), cts.Token, TaskCreationOptions.None, TaskScheduler.Default);
}
private void Stop_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
MessageBox.Show("Processing cancelled");
}
}
public void LongRunningFunction(string String, CancellationToken token)
{
//Long running processing
//...
MessageBox.Show("Processing finished");
}
}
Update:
The only what I changed is the way I declare the function and added an if statement inside the while loop
that is inside the function. Is like below:
The CancelationToken was added in order to be able to stop the processing when Stop button is pressed.
Without thread I declare the function like this:
public void LongRunningFunction(string String)
{
while (condition)
{
//My code within While loop
}
MessageBox.Show("Processing finished");
}
and with Thread I define the function like this:
public void LongRunningFunction(string String, CancellationToken token)
{
while (condition)
{
if (token.IsCancellationRequested)
{
break;
}
//My code within While loop
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
Update2:
Inside LongRunningFunction() is called another function that prints the lines. Is like below.
public void LongRunningFunction(string fileName, CancellationToken token)
{
StreamWriter writer = new StreamWriter(#outputfile, true, Encoding.UTF8, 4096);
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
List<byte> buffer = new List<byte>();
List<string> buffer1 = new List<string>();
SoapHexBinary hex = new SoapHexBinary();
while (chunk.Length > 0)
{
if (token.IsCancellationRequested) // ### For Cancel Thread ###
{
break;
} // ### For Cancel Thread ###
chunk = reader.ReadBytes(1024);
foreach (byte data in chunk)
{
if (somecondition)
{
buffer.Add(data);
}
else if (other condition)
{
buffer.Add(data);
PrintFunction(buffer, hex, outputfile, writer); // Print Line
}
else if (some other condition)
{
buffer.Add(data);
}
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
if (writer != null)
{
writer.Dispose();
writer.Close();
}
}
private void PrintFunction(List<byte> buffer, SoapHexBinary hex, string outputfile, StreamWriter writer)
{
if (buffer.Count > 0)
{
if (buffer.Count >= lowlimit)
{
hex.Value = buffer.ToArray();
string Register = hex.ToString();
Regex pattern1 = new Regex(#"some pattern");
if (pattern1.IsMatch(Register))
{
Match l1 = Regex.Match(Register, #"somepattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
writer.Write("{0}|{1}|{2}", Convert.ToInt32(l1.Groups[1].ToString(), 16), l1.Groups[2].Value, l1.Groups[3].Value);
Match l2 = Regex.Match(Register, #"otherpattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (l2.Success)
{
foreach (Match m in Regex.Matches(l2.Groups[2].ToString(), pattern2, RegexOptions.IgnoreCase | RegexOptions.Compiled))
{
//Some foreach code
}
foreach (Match x in Regex.Matches(var, #"pattern"))
{
//come code
}
writer.WriteLine("," + String.Join(",", var1));
}
else
{
writer.WriteLine();
}
}
}
}
buffer.Clear();
}
Update3:
Hi bebosh,
I still have doubts how to apply in my function, the way you define the delegate in your example function.
My function looks like this:
public void LongRunningFunction(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
}
It could be something like this or how?:
private void LongRunningFunction(string fileName)
{
MethodInvoker action = delegate
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
};
}
Bebosh's answer was good enough. To increase the performance further you can set the ThreadPriority of the "thread" by setting the ".Priority = ThreadPriority.AboveNormal" right after setting the "thread.IsBackground = true;".
Could you try this code:
bool Stop = false;
Thread thread;
private void StartButton_Click(object sender, EventArgs e)
{
string FileName = #"...\a.bin";
thread = new Thread(new ThreadStart(() => DoLongProcess(FileName)));
thread.IsBackground = true;
thread.Start();
}
private void StopButton_Click(object sender, EventArgs e)
{
Stop = true;
}
private void DoLongProcess(string file)
{
using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open)))
{
int pos = 0;
int length = (int)reader.BaseStream.Length;
while (pos < length)
{
if (Stop)
thread.Abort();
// using Invoke if you want cross UI objects
this.Invoke((MethodInvoker)delegate
{
label1.Text = pos.ToString();
});
pos += sizeof(int);
}
}
}
use interrupting the thread
Thread thread;
public MainWindow()
{
InitializeComponent();
}
private async void StartButtonClick(object sender, RoutedEventArgs e)
{
thread = new Thread(ExecuteLong);
var task = Task.Run(() =>
thread.Start());
await task;
}
private void ExecuteLong()
{
try
{
// long task
}
catch (ThreadInterruptedException e)
{
MessageBox.Show("cancelled!");
return;
}
MessageBox.Show("finished");
}
private void CancelButtonClick(object sender, RoutedEventArgs e)
{
this.thread.Interrupt();
}
I am trying to call a nested Asynchronous function but I am not getting the required data.
Since I am using a wcf service with Silverlight I can only use Asynchronous functions.
In my code I am saving a set of rows containing userdata. Before I save it I need to check the username is unique. Now I only need to find out the first one and then break out of loop and show a message to the user.for simplicity sake, I have stripped the function of all the extra data and this is how it looks
private void SaveUsers(bool CloseForm)
{
ObservableCollection<User> _UpdatedUsers = new ObservableCollection<User>();
DatabaseServiceLocal _dataService = new DatabaseServiceLocal(Database);
foreach (UserViewModel _User in _AllUsers)
{
//bool success = _dataService.IsUserNameUnique(_User.UserName, _User.UserID, Database.CurrentClient.ClientID);
if (_User.Dirty && !_User.IsBlank)
{
_dataService.CheckIsUserNameUnique += (s, e) =>
{
if (e.IsUnique)
_UpdatedUsers.Add(_User.SaveAsUser());
else
{
_UpdatedUsers = new ObservableCollection<User>();
csaMessageBox.Show(string.Format("Username {0} is not allowed as it already exists in the system. Please choose a different username.", ""), null);
return;
}
};
_dataService.IsUserNameUnique(_User.UserName, _User.UserID, Database.CurrentClient.ClientID);
}
_dataService.UpdateStaffAndUsersCompleted += (s, e) =>
{
BusyIndicator = false;
if (e.Success)
{
}
if (CloseForm)
ReturnToHomePage();
else
{
LoadUsers();
OnUsersSaved();
}
}
BusyIndicator = true;
BusyMessage = "Saving...";
_dataService.UpdateUsers(Database.CurrentProject.ProjectID, Database.CurrentClient.ClientID, _UpdatedUsers, _DeletedProjectUsers);
}
In this case I am trying to find if the username is unique,show user a message and return.
Obviously it's not as simple as that.I have tried a couple more different ways but it didn't work. How do I get this working?
I think you can make your life easier by adding a couple of helper functions. The first one is an asynchronous function that checks whether a user is unique. You may need to add some code to set tcs.SetException if there is an error.
private Task<bool> IsUserUniqueAsync(UserViewModel user, DatabaseServiceLocal dataService)
{
var tcs = new TaskCompletionSource<bool>();
dataService.CheckIsUserNameUnique += (s, e) =>
{
tcs.SetResult(e.IsUnique);
};
dataService.IsUserNameUnique(user.UserName, user.UserID, Database.CurrentClient.ClientID);
return tcs.Task;
}
The second one updates all the users asynchrnously
public Task<bool> UpdateUsersAsync(ObservableCollection<User> updatedUsers, DatabaseServiceLocal dataService)
{
var tcs = new TaskCompletionSource<bool>();
BusyIndicator = true;
BusyMessage = "Saving...";
dataService.UpdateStaffAndUsersCompleted += (s, e) =>
{
BusyIndicator = false;
tcs.SetResult(e.Success);
};
dataService.UpdateUsers(Database.CurrentProject.ProjectID, Database.CurrentClient.ClientID, updatedUsers, _DeletedProjectUsers);
return tcs.Task;
}
Then your SaveUsers method becomes a bit simpler.
private async void SaveUsers(bool CloseForm)
{
ObservableCollection<User> _UpdatedUsers = new ObservableCollection<User>();
DatabaseServiceLocal _dataService = new DatabaseServiceLocal(Database);
Dictionary<Task<bool>, User> tasks = new Dictionary<Task<bool>, User>();
// start all tasks in parallel
foreach (UserViewModel _User in _AllUsers)
{
if (_User.Dirty && !_User.IsBlank)
{
tasks.Add(IsUserUniqueAsync(_User, _dataService), _User);
}
}
// process each task as it completes
while(tasks.Count() > 0 )
{
var task = await Task.WhenAny(tasks.Keys.ToArray());
if(task.Result)
{
_UpdatedUsers.Add(_User.SaveAsUser());
}
else
{
MessageBox.Show(string.Format("Username {0} is not allowed as it already exists in the system. Please choose a different username.", ""), null);
return;
}
tasks.Remove(task);
}
if( await UpdateUsersAsync(_UpdatedUsers, _dataService))
{
if (CloseForm)
ReturnToHomePage();
else
{
LoadUsers();
OnUsersSaved();
}
}
}
Your code would more or less look like this.
ObservableCollection<User> _UpdatedUsers = new ObservableCollection<User>();
int _verifiedUsersCount = 0;
DatabaseServiceLocal _dataService = new DatabaseServiceLocal(Database);
//Verify unique users
private void SaveUsers(bool CloseForm)
{
_dataService.CheckIsUserNameUnique += CheckIsUserNameUnique;
foreach (UserViewModel _User in _AllUsers)
{
//bool success = _dataService.IsUserNameUnique(_User.UserName, _User.UserID, Database.CurrentClient.ClientID);
if (_User.Dirty && !_User.IsBlank)
{
_dataService.IsUserNameUnique(_User.UserName, _User.UserID, Database.CurrentClient.ClientID);
}
}
}
//Store verified users to save
private void CheckIsUserNameUnique(object s, CheckIsUserNameUniqueEventArgs e)
{
if (e.IsUnique)
_UpdatedUsers.Add(_User.SaveAsUser());
else
{
csaMessageBox.Show(string.Format("Username {0} is not allowed as it already exists in the system. Please choose a different username.", ""), null);
}
verifiedUsersCount++;
//Call after all the users have been verified for uniqueness
if (_AllUsers.Count() == verifiedUsersCount)
{
OnUniqueUserVerifyComplete();
}
}
//Save verified users
private void OnUniqueUserVerifyComplete()
{
//No unique users
if (_UpdatedUsers.Count < 1) { return; }
_dataService.UpdateStaffAndUsersCompleted += (s, e) =>
{
BusyIndicator = false;
if (e.Success)
{
}
if (CloseForm)
ReturnToHomePage();
else
{
LoadUsers();
OnUsersSaved();
}
};
BusyIndicator = true;
BusyMessage = "Saving...";
_dataService.UpdateUsers(Database.CurrentProject.ProjectID, Database.CurrentClient.ClientID, _UpdatedUsers, _DeletedProjectUsers);
}