I have a method that doing Chromium browser initialization. After running two asynchronous method, the program automatically close. Did I use the Task.WhenAll wrong?
Here is the entry point of the program:
Start.cs
static class Start
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Initializer.Start();
}
}
Initializer.cs
public static class Initializer
{
public static async void Start()
{
// The task that automatically close the program.
Task<bool> chromiumInitTask = ChromiumInfo.InitializeAsync();
await chromiumInitTask;
Task webviewInitTask = WebViewInfo.InitializeAsync();
Task guiInitTask = GUIInfo.InitializeAsync();
HardwareManager.Initialize();
await webviewInitTask;
await guiInitTask;
GUIInfo.Layout.ChangeMainDisplay(ChromiumInfo.Browser);
Application.Run(GUIInfo.Layout.GetLayoutForm());
}
}
ChromiumInfo.cs
public static class ChromiumInfo
{
private static CefSettings _settings;
private static ChromiumWebBrowser _browser;
private static BrowserSettings _browserSettings;
private static Dictionary<string, bool> _initTasks = new Dictionary<string, bool>()
{
{ "Settings", false },
{ "Browser Settings", false },
{ "Browser", false },
{ "Cef", false }
};
private static KeyboardHandler _keyboardHandler;
/// <summary>
/// An setting which using on initialize.
/// </summary>
public static CefSettings Setting => _settings;
/// <summary>
/// Representing a browser which can be show in UI.
/// </summary>
public static ChromiumWebBrowser Browser => _browser;
/// <summary>
/// A keyboard handler which handle various keyboard events.
/// </summary>
public static KeyboardHandler KeyboardHandler => _keyboardHandler;
/// <summary>
/// Occur when request to change the browser.
/// </summary>
public static event RequestChangeBrowserEventHandler RequestChangeBrowser;
/// <summary>
/// Initialize all Chromium components in asynchronously.
/// </summary>
/// <param name="initAllScripts">Indicate should initialize all scripts contain in the root script directory.</param>
/// <returns>Return a task can be awaited. True means sucess. Otherwise, return false.</returns>
public static async Task<bool> InitializeAsync(bool initAllScripts = true)
{
Task settingInit = SettingsInitializeAsync();
Task browserSettingInit = BrowserSettingsInitializeAsync();
// The below line that automatically close the program.
await Task.WhenAll(settingInit, browserSettingInit);
Task cefInit = Cef.InitializeAsync(_settings);
await cefInit;
_initTasks["Cef"] = true;
Task browserInit = BrowserInitializeAsync();
await browserInit;
Task eventInit = EventInitializeAsync();
await eventInit;
Task scriptInit = ScriptInitializeAsync();
await scriptInit;
return _initTasks.Values.Where(it => it).Count() == _initTasks.Count;
}
private static async Task SettingsInitializeAsync()
{
try
{
_settings = new CefSettings();
_settings.CommandLineArgsDisabled = false;
_settings.CefCommandLineArgs.Clear();
_settings.CefCommandLineArgs.Add("enable-3d-apis", "1");
_settings.CefCommandLineArgs.Add("enable-webgl-draft-extensions", "1");
_settings.CefCommandLineArgs.Add("enable-gpu", "1");
_settings.CefCommandLineArgs.Add("enable-webgl", "1");
_settings.CefCommandLineArgs.Add("gpu_compositing", "1");
_settings.CefCommandLineArgs.Add("ignore-gpu-blocklist", "1");
await Task.Delay(1000).ConfigureAwait(false);
_initTasks["Settings"] = true;
}
catch (Exception e)
{
SystemLog.Write(e);
}
}
private static async Task BrowserSettingsInitializeAsync()
{
try
{
_browserSettings = new BrowserSettings();
_browserSettings.WebGl = CefState.Enabled;
await Task.Delay(1000).ConfigureAwait(false);
_initTasks["Browser Settings"] = true;
}
catch (Exception e)
{
SystemLog.Write(e);
}
}
private static async Task BrowserInitializeAsync()
{
try
{
_browser = new ChromiumWebBrowser(Properties.Settings.Default.DefaultURL);
_browser.BrowserSettings = _browserSettings;
_browser.Dock = System.Windows.Forms.DockStyle.Fill;
await Task.Delay(1000).ConfigureAwait(false);
_initTasks["Browser"] = true;
}
catch (Exception e)
{
SystemLog.Write(e);
}
}
private static async Task EventInitializeAsync()
{
KeyboardHandler keyboardHandler = new KeyboardHandler();
WebCommandHandler commandHandler = new WebCommandHandler();
_browser.ConsoleMessage += keyboardHandler.Handle;
_browser.ConsoleMessage += commandHandler.Handle;
_browser.AddressChanged += Custom_AddressChanged;
_keyboardHandler = keyboardHandler;
await Task.Delay(1000).ConfigureAwait(false);
}
private static async Task ScriptInitializeAsync()
{
string scriptPath = $#"{ProgramInfo.RootPath}\scripts";
if (Directory.Exists(scriptPath))
{
var files = Directory.GetFiles(scriptPath, "*.js");
files?.ToList().ForEach(f => _browser.GetMainFrame().ExecuteJavaScriptAsync(f, _browser.Address));
}
await Task.Delay(1000).ConfigureAwait(false);
}
private static void Custom_AddressChanged(object sender, AddressChangedEventArgs e)
{
var wv2SiteCount = Properties.Settings.Default.WebToWV2.Cast<string>()
.Where(s => s.IndexOf(e.Address) >= 0).Count();
if (wv2SiteCount > 0)
{
WebViewInfo.Navigate(e.Address);
RequestChangeBrowser?.Invoke(null, new RequestChangeBrowserEventArgs(WebViewInfo.Browser));
}
}
}
This is because of how await works. When await acts on a Task that is incomplete, it returns. So your program is working like this:
Main runs, and calls Initializer.Start()
Initializer.Start() runs and calls ChromiumInfo.InitializeAsync()
ChromiumInfo.InitializeAsync() runs until it calls await Task.WhenAll(settingInit, browserSettingInit)
Because Task.WhenAll returns an incomplete Task, ChromiumInfo.InitializeAsync() returns its own incomplete Task and execution returns to Initializer.Start().
The await in Initializer.Start() sees the incomplete Task and returns its own incomplete Task and execution returns to Main().
Because Main() doesn't act on the Task returned by Initializer.Start(), execution continues to the next line, which is the end of the program.
The solution is fairly simple: Change your Main method to async and use await.
public static async Task Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
await Initializer.Start();
}
The ability to make Main() async is a feature introduced in C# 7.1.
Related
I am using Unity 3D and I want to run task in main thread and await it to complete.
Here is my code:
public override Task<Vibranium.Protobuffers.ACSpawnData> WorldObjectSpawned(Vibranium.Protobuffers.WorldObjectData request, ServerCallContext context)
{
ACSpawnData acSpawnData = new ACSpawnData();
IEnumerator SpawnGo(ACSpawnData acSpawnData)
{
int spawnId = WorldObjectManager.Instance.GetWorldObjects.Count + 1;
GameObject spawnedGO = GameObject.CreatePrimitive(PrimitiveType.Cube);
spawnedGO.name = spawnId.ToString();
spawnedGO.transform.SetParent(WorldObjectManager.Instance.transform);
if (request.WorldObjectType == WorldObjectType.Player)
{
spawnedGO.GetComponent<Renderer>().material.color = Color.red;
}
WorldObjectManager.Instance.GetWorldObjects.Add(spawnedGO);
acSpawnData.SpawnId = (uint)spawnId;
acSpawnData.WorldObjectType = request.WorldObjectType;
yield return null;
}
MainThreadDispatcher.Instance().Enqueue(SpawnGo(acSpawnData));
return Task.FromResult(acSpawnData);
}
To execute IEnumerator in main thread I use this:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
/// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher
/// <summary>
/// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for
/// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling
/// </summary>
public class MainThreadDispatcher : MonoBehaviour {
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
public void Update() {
lock(_executionQueue) {
while (_executionQueue.Count > 0) {
_executionQueue.Dequeue().Invoke();
}
}
}
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <param name="action">IEnumerator function that will be executed from the main thread.</param>
public void Enqueue(IEnumerator action) {
lock (_executionQueue) {
_executionQueue.Enqueue (() => {
StartCoroutine (action);
});
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
Enqueue(ActionWrapper(action));
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <param name="action">function that will be executed from the main thread.</param>
/// <returns>A Task that can be awaited until the action completes</returns>
public Task EnqueueAsync(Action action)
{
var tcs = new TaskCompletionSource<bool>();
void WrappedAction() {
try
{
action();
tcs.TrySetResult(true);
} catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
Enqueue(ActionWrapper(WrappedAction));
return tcs.Task;
}
IEnumerator ActionWrapper(Action a)
{
a();
yield return null;
}
private static MainThreadDispatcher _instance = null;
public static bool Exists() {
return _instance != null;
}
public static MainThreadDispatcher Instance() {
if (!Exists ()) {
throw new Exception ("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene.");
}
return _instance;
}
void Awake() {
if (_instance == null) {
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
void OnDestroy() {
_instance = null;
}
}
Everything works fine. The gameobject is created however Task.FromResult does not await SpawnGo to populate acSpawnData so acSpawnData is always returned empty.
How can I block the execution of Task WorldObjectSpawned until IEnumerator SpawnGo does it's job and populate acSpawnData so I can return it?
Instead of doing this directly with your own threading, you should utilize C#'s built-in async/await.
It allows you to spin off tasks, which manage the threads for you, and then you can decide when you need to wait for a task to complete before proceeding.
As a side benefit, it will also significantly streamline and reduce your code.
I have a switch language helper, I wrote like
public static async Task SelectLanguage()
{
//Some extra code
string strTitle = await LanService.Get("select_language");
ListDialogControl dialog = new ListDialogControl(strTitle, list, index);
dialog.Show();
dialog.Result += async (s) =>
{
**//How to pass this returned variable s to SelectLanguage()**
};
}
Now I want to change SelectLanguage() from Task to Task《string》, which return my dialog's result.
And for ListDialogControl, it's a user control.
public sealed partial class ListDialogControl : UserControl
{
Popup popup;
int ListSelectedIndex;
List<string> myList = new List<string>();
public Action<string> Result { get; set; }
/// <summary>
/// Title
/// List<string>
/// selectedIndex, -1 no need to select. if u want to select an item, selectedIndex must >= 0
/// </summary>
/// <param name="strTitle"></param>
/// <param name="list"></param>
/// <param name="selectedIndex"></param>
public ListDialogControl(string strTitle, List<string> list, int selectedIndex = -1, bool showTick = true)
{
this.InitializeComponent();
popup = Globals.popup;
popup.Child = this;
popup.Closed += Popup_Closed;
this.Loaded += ControlWindow_Loaded;
this.Unloaded += ControlWindow_Unloaded;
this.PreviewKeyDown += ControlWindow_PreviewKeyDown;
Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated;
this.Width = Window.Current.Bounds.Width;
this.Height = Window.Current.Bounds.Height;
TextBlockTip.Text = strTitle;
myList = list;
}
#region ShowHide
public void Show()
{
popup.IsOpen = true;
}
public void Hide(string result)
{
if (popup.IsOpen == false)
return;
popup.IsOpen = false;
myList.Clear();
TextBlockTip.Text = "";
Result?.Invoke(result);
}
#endregion
}
You can use a TaskCompletionSource to achive this. Here are two options how you can implement this. Be sure to read the docs about it to ensure, you know how to handle the different states of a task.
Option 1: Use a TaskCompletionSource inside the method
public static async Task SelectLanguage()
{
var tcs = new TaskCompletionSource<string>();
ListDialogControl dialog = new ListDialogControl(strTitle, list, index);
dialog.Show();
dialog.Result += async (s) =>
{
// This will notify the caller that the task just completed
tcs.SetResult(s);
};
// Here you can wait until the task completes
var yourResult = await tcs.Task;
}
Option 2: Use a TaskCompletionSource inside the UserControl
public class YourUserControl : UserControl
{
private TaskCompletionSource<string> _resultTask;
public Task<string> Show()
{
_resultTask = new TaskCompletionSource<string>();
return _resultTask.Task;
}
public void Hide(string result)
{
_resultTask.SetResult(result);
}
}
And now you don't need to create a TaskCompletionSource everytime:
public async Task TestMethod()
{
var dialog = new YourUserControl();
// Show dialog and wait here until the usercontrol completes the task
var result= await dialog.Show();
}
An alternative to using a TaskCompletionSource is to use a SemaphoreSlim to wait asynchronously until you have a value to return:
public static async Task<string> SelectLanguage()
{
string strTitle = await LanService.Get("select_language");
string result = null;
using (SemaphoreSlim semaphore = new SemaphoreSlim(0, 1))
{
ListDialogControl dialog = new ListDialogControl(strTitle, list, index);
dialog.Show();
dialog.Result += async (s) =>
{
//...
result = s;
semaphore.Release();
};
await semaphore.WaitAsync();
}
return result;
}
So I need to have a timer to count down from 60 seconds. I am new to Xamarin and have no idea what it accepts. It will be used in Android.
Any suggestions on how to start?
Can you use System.Timers.Timer?
You can use the System.Threading.Timer class, which is documented in the Xamarin docs: https://developer.xamarin.com/api/type/System.Threading.Timer/
Alternatively, for Xamarin.Forms, you have access to a cross-platform Timer via the Device class:
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
// called every 1 second
// do stuff here
return true; // return true to repeat counting, false to stop timer
});
If you just need Android you can use
System.Threading.Timer
In shared code with Xamarin Forms you can use
Device.StartTimer(...)
Or you can implement one yourselfe with advanced features like:
public sealed class Timer : CancellationTokenSource {
private readonly Action _callback;
private int _millisecondsDueTime;
private readonly int _millisecondsPeriod;
public Timer(Action callback, int millisecondsDueTime) {
_callback = callback;
_millisecondsDueTime = millisecondsDueTime;
_millisecondsPeriod = -1;
Start();
}
public Timer(Action callback, int millisecondsDueTime, int millisecondsPeriod) {
_callback = callback;
_millisecondsDueTime = millisecondsDueTime;
_millisecondsPeriod = millisecondsPeriod;
Start();
}
private void Start() {
Task.Run(() => {
if (_millisecondsDueTime <= 0) {
_millisecondsDueTime = 1;
}
Task.Delay(_millisecondsDueTime, Token).Wait();
while (!IsCancellationRequested) {
//TODO handle Errors - Actually the Callback should handle the Error but if not we could do it for the callback here
_callback();
if(_millisecondsPeriod <= 0) break;
if (_millisecondsPeriod > 0 && !IsCancellationRequested) {
Task.Delay(_millisecondsPeriod, Token).Wait();
}
}
});
}
public void Stop() {
Cancel();
}
protected override void Dispose(bool disposing) {
if (disposing) {
Cancel();
}
base.Dispose(disposing);
}
}
Yes, you can use System.Timers.Timer.
In Android we can use java.util.Timer like this:
private int i;
final Timer timer=new Timer();
TimerTask task=new TimerTask() {
#Override
public void run() {
if (i < 60) {
i++;
} else {
timer.cancel();
}
Log.e("time=",i+"");
}
};
timer.schedule(task, 0,1000);
In Xamarin.Android we can also use java.util.Timer like this:
[Activity(Label = "Tim", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
Timer timer = new Timer();
timer.Schedule(new MyTask(timer),0,1000);
}
}
class MyTask : TimerTask
{
Timer mTimer;
public MyTask(Timer timer) {
this.mTimer = timer;
}
int i;
public override void Run()
{
if (i < 60)
{
i++;
}
else {
mTimer.Cancel();
}
Android.Util.Log.Error("time",i+"");
}
}
You can always use
Task.Factory.StartNewTaskContinuously(YourMethod, new CancellationToken(), TimeSpan.FromMinutes(10));
Be sure to add the following class to your project
static class Extensions
{
/// <summary>
/// Start a new task that will run continuously for a given amount of time.
/// </summary>
/// <param name="taskFactory">Provides access to factory methods for creating System.Threading.Tasks.Task and System.Threading.Tasks.Task`1 instances.</param>
/// <param name="action">The action delegate to execute asynchronously.</param>
/// <param name="cancellationToken">The System.Threading.Tasks.TaskFactory.CancellationToken that will be assigned to the new task.</param>
/// <param name="timeSpan">The interval between invocations of the callback.</param>
/// <returns>The started System.Threading.Tasks.Task.</returns>
public static Task StartNewTaskContinuously(this TaskFactory taskFactory, Action action, CancellationToken cancellationToken, TimeSpan timeSpan
, string taskName = "")
{
return taskFactory.StartNew(async () =>
{
if (!string.IsNullOrEmpty(taskName))
{
Debug.WriteLine("Started task " + taskName);
}
while (!cancellationToken.IsCancellationRequested)
{
action();
try
{
await Task.Delay(timeSpan, cancellationToken);
}
catch (Exception e)
{
break;
}
}
if (!string.IsNullOrEmpty(taskName))
{
Debug.WriteLine("Finished task " + taskName);
}
});
}
}
I have SinglaR hub class "MyHub" and a .net client class that connects to it successfully in runtime. however when i'm testing the client with nunit test it doesn't connect, I've debugged the test and find that hub instance doesn't created by SinglaR's hub activator class.
I'm using Resharper nunit runner for running test.
Here is my hub class structure, i've remove method's body for simplicity.
[HubName("myHub")]
public class MyHub : Hub
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MyHub));
#region MyRegion
#region Connection Methods
public MyHub()
{
}
private static int c;
public override Task OnConnected()
{
}
private string UserNameConnectionId
{
}
public override Task OnReconnected()
{
lock (LockObject)
{
}
}
public override Task OnDisconnected(bool b)
{
lock (LockObject)
{
}
}
private void LogUserActivity(string userName, string activity)
{
}
#endregion
//Live controls invoke this Method to send their required data and also their ConnectionId
/// <summary>
/// </summary>
/// <param name="liveControlsInfo"></param>
public Task UpdateLiveControlsInfo(LiveControlInfo[] liveControlsInfo)
{
lock (LockObject)
{
}
}
public string GetValue()
{
return "SignalR Rox!";
}
public void Acknowledge()
{
lock (LockObject)
{
string connectionId = Context.ConnectionId;
}
}
}
#endregion
}
}
here is HubWinClient class
public class HubWinClient
{
private readonly string _userName;
public int ServerPort { get; set; }
private static readonly ILog logger = LogManager.GetLogger(typeof (HubWinClient));
protected int HubLogCounter = 0;
private HubConnection connection;
private bool moduleStarted;
private IHubProxy myHub;
public HubWinClient(int serverPort, string userName= "Windows Hub Client", bool tryConnect=false)
{
_userName = userName;
ServerPort = serverPort;
}
public void Start()
{
ConnectToHub();
moduleStarted = true;
}
public void Stop()
{
DisconnectFromHub();
moduleStarted = false;
}
public void ConnectToHub()
{
ConnectToHub(false);
}
public event EventHandler<EventArgs> ConnectingToHub = delegate { };
public event EventHandler<EventArgs> ConnectedToHub = delegate { };
/// <summary>
/// callback event called by hubhost
/// </summary>
public event EventHandler<int> LogSentToHub = delegate { };
public event EventHandler<EventArgs> DisonnectedFromHub = delegate { };
public event EventHandler<EventArgs> HubConnectionError = delegate { };
public event EventHandler<EventArgs> HubInvokeError = delegate { };
public event EventHandler<EventArgs> HubInvokeSuccess = delegate { };
public event EventHandler<EventArgs> NotConnectedToSentLog = delegate { };
public event EventHandler<LogMessage> DataRecivedFromHub = delegate { };
/// <summary>
/// connects HunNotifier to Hun in order to ListenerMessage passing can be done
/// </summary>
/// <param name="reconnect"></param>
private void ConnectToHub(bool reconnect)
{
//This url will not point to a specific connection. But will instead point to the root of your site=root of hub server.
string hubAddress = "http://localhost:" + ServerPort;
connection = new HubConnection(hubAddress, new Dictionary<string, string>{{"UserName", _userName } });
ConnectingToHub(this, new EventArgs());
if (reconnect)
logger.InfoFormat("HubWinClient reconnecting to HubHost # {0}...", hubAddress);
else
logger.InfoFormat("HubWinClient connecting to HubHost # {0}...", hubAddress);
myHub = connection.CreateHubProxy("myHub");
myHub.On("joined", joined);
myHub.On("rejoined", rejoined);
myHub.On("leaved", leaved);
myHub.On<LogMessage>("addData", addData);
myHub.On<int>("LogSentToWeb", LogSentToWeb);
connection.StateChanged += change =>
{
var a = change;
var b = 10;
var a1 = connection.LastError;
var b2 = connection.Proxy;
};
try
{
connection.Start();
}
catch (AggregateException aggregateException)
{
Exception exception = aggregateException.InnerException;
logger.Error("HubWinClient connection to HubHost exception...", exception);
HubConnectionError(this, new MyEventArgs {Exception = exception});
}
catch (Exception exception)
{
logger.Error("HubWinClient connection to HubHost exception...", exception);
HubConnectionError(this, new MyEventArgs {Exception = exception});
}
}
public void DisconnectFromHub()
{
connection.Stop();
}
/// <summary>
/// when hub notifier connected to hub, hub invoke this method to inform connection success.
/// </summary>
public void joined()
{
var liveControlInfo = new LiveControlInfo
{
LiveControlType = LiveControlType.mainClient
};
// myHub.Invoke("AddLiveControl", liveControlInfo).Wait();
// logger.Info("HubWinClient joined");
ConnectedToHub(this, new EventArgs());
}
/// <summary>
/// when hub notifier reconnected to hub, hub invoke this method to inform reconnection success.
/// </summary>
public void rejoined()
{
logger.Info("HubWinClient rejoined");
}
/// <summary>
/// when hub notifier disconnect from hub, hub invoke this method to inform disconnection.
/// </summary>
public void leaved()
{
logger.Info("HubWinClient disconnected");
DisonnectedFromHub(this, new EventArgs());
}
public void addData(LogMessage logMessage)
{
DataRecivedFromHub(this, logMessage);
}
public event EventHandler<LogMessage> OnSendingLogToHub = delegate { };
/// <summary>
/// called by HubClientMoudle when it's ApplyLogReceived are invoked by WCF service
/// </summary>
/// <param name="logMessage"></param>
public void SendToHub(LogMessage logMessage)
{
OnSendingLogToHub(this, logMessage);
if (!moduleStarted) return;
if (connection.State != ConnectionState.Connected)
ConnectToHub(true);
if (connection.State == ConnectionState.Connected)
{
if (System.Diagnostics.Debugger.IsAttached ||
Math.Abs((logMessage.Log.ArrivalDateTime - DateTime.Now).Ticks) < TimeSpan.FromMinutes(10).Ticks)
{
try
{
Task task = myHub.Invoke("send", logMessage);
task.Wait(5000);
logger.Debug("Send log success");
HubInvokeSuccess(this, new EventArgs());
}
catch (AggregateException aggregateException)
{
Exception exception = aggregateException.InnerException;
logger.Error("Send log Exceotion!", exception);
HubInvokeError(this, new MyEventArgs {Exception = exception});
}
catch (Exception exception)
{
logger.Error("Send log Exceotion!", exception);
HubInvokeError(this, new MyEventArgs {Exception = exception});
}
}
else
{
logger.Warn("Log arrival date time is not less than DateTime.Now+30 minutues");
}
}
else
{
logger.Warn("HubWinClient not connected to send log");
NotConnectedToSentLog(this, new EventArgs());
}
}
/// <summary>
/// when Hub receives log from HubWinClient, calls this method to inform send success
/// </summary>
/// <param name="log"></param>
public void LogSentToWeb(int logId)
{
logger.Debug("Call back from Hub: send success");
LogSentToHub(this, logId);
}
}
Here is my test:
[Category("Integration")]
public class HubWinClientTests
{
[Test]
public void TestConnection()
{
//arrange
const int port = 4000;
HubHost hubHost = new HubHost(port);
HubWinClient hubWinClient = new HubWinClient(port,"mvc app client");
bool clientConnected = false;
bool clientDisconnected = false;
bool hubInvokeError = false;
hubWinClient.ConnectedToHub += (sender, args) => { clientConnected = true; };
hubWinClient.DisonnectedFromHub += (sender, args) => { clientDisconnected = true; };
hubWinClient.HubInvokeError += (sender, args) => { hubInvokeError = true; };
//act
hubHost.Start();
hubWinClient.Start();
//assert
var constrain = Is.True.After(10000, 100);
Assert.That(() => clientConnected, constrain);
//act
hubWinClient.SendToHub(new LogMessage());
//assert
var constrain2 = Is.True.After(2000, 100);
Assert.That(() => hubInvokeError == false, constrain2);
// act
hubWinClient.DisconnectFromHub();
// assert
var constrain1 = Is.True.After(2000, 100);
Assert.That(() => clientDisconnected, constrain1);
hubHost.Stop();
}
}
For some reason when i run my the test by resharper nunit runner, hub instance does created and my test fails. i think it's related to Resharper's and how it create test environment (i'e process creation and such stuff).
Any ideas.
Thanks.
I have a producer/consumer queue as following but I am getting ArgumentWException.
Following is the code:
public class ProducerConsumer<T> where T : class
{
#region Private Variables
private Thread _workerThread;
private readonly Queue<T> _workQueue;
private object _enqueueItemLocker = new object();
private object _processRecordLocker = new object();
private readonly Action<T> _workCallbackAction;
private AutoResetEvent _workerWaitSignal;
#endregion
#region Constructor
public ProducerConsumer(Action<T> action)
{
_workQueue = new Queue<T>();
_workCallbackAction = action;
}
#endregion
#region Private Methods
private void ProcessRecord()
{
while (true)
{
T workItemToBeProcessed = default(T);
bool hasSomeWorkItem = false;
lock (_processRecordLocker)
{
hasSomeWorkItem = _workQueue.Count > 0;
if (hasSomeWorkItem)
{
workItemToBeProcessed = _workQueue.Dequeue();
if (workItemToBeProcessed == null)
{
return;
}
}
}
if (hasSomeWorkItem)
{
if (_workCallbackAction != null)
{
_workCallbackAction(workItemToBeProcessed);
}
}
else
{
_workerWaitSignal.WaitOne();
}
}
}
#endregion
#region Public Methods
/// <summary>
/// Enqueues work item in the queue.
/// </summary>
/// <param name="workItem">The work item.</param>
public void EnQueueWorkItem(T workItem)
{
lock (_enqueueItemLocker)
{
_workQueue.Enqueue(workItem);
if (_workerWaitSignal == null)
{
_workerWaitSignal = new AutoResetEvent(false);
}
_workerWaitSignal.Set();
}
}
/// <summary>
/// Stops the processer, releases wait handles.
/// </summary>
/// <param name="stopSignal">The stop signal.</param>
public void StopProcesser(AutoResetEvent stopSignal)
{
EnQueueWorkItem(null);
_workerThread.Join();
_workerWaitSignal.Close();
_workerWaitSignal = null;
if (stopSignal != null)
{
stopSignal.Set();
}
}
/// <summary>
/// Starts the processer, starts a new worker thread.
/// </summary>
public void StartProcesser()
{
if (_workerWaitSignal == null)
{
_workerWaitSignal = new AutoResetEvent(false);
}
_workerThread = new Thread(ProcessRecord) { IsBackground = true };
_workerThread.Start();
}
#endregion
}
Another class is:
public class Tester
{
private readonly ProducerConsumer<byte[]> _proConsumer;
public Tester()
{
_proConsumer = new ProducerConsumer<byte[]>(Display);
}
public void AddData(byte[] data)
{
try
{
_proConsumer.EnQueueWorkItem(recordData);
}
catch (NullReferenceException nre)
{
}
}
public void Start()
{
_proConsumer.StartProcesser();
}
private static object _recordLocker = new object();
private void Display(byte[] recordByteStream)
{
try
{
lock (_recordLocker)
{
Console.WriteLine("Done with data:" + BitConverter.ToInt32(recordByteStream, 0));
}
}
catch (Exception ex)
{
}
}
}
And my main function:
class Program
{
private static Tester _recorder;
static void Main(string[] args)
{
_recorder = new Tester();
_recorder.StartRecording();
for (int i = 0; i < 100000; i++)
{
_recorder.AddRecordData(BitConverter.GetBytes(i));
}
Console.Read();
}
}
Any idea why do I get the exception and what should I do to avoid that ?
Your class, in its current implementation, is not thread-safe. You're using two different objects for your Enqueue (lock (_enqueueItemLocker)) and Dequeue (lock (_processRecordLocker)) calls, which creates a race condition in your Queue<T>.
You need to lock the same object instance on both calls in order to safely use the queue.
If you're using .NET 4, I'd recommend either using ConcurrentQueue<T> or BlockingCollection<T> instead, as these would eliminate the need for the locks in your code, since they're thread-safe.