I have a Silverlight 5 browser application.
There is a class
public class ActivityControl:UserControl {
public void LoadSubControls() {
//Creates Other UserControls, does calculations and is very slow..No refactoring..
}
}
I need to create multiple instances of this class and call the method LoadSubControls on runtime.
public class BasicContainer:UserControl {
public void CreateMultipleActivityControls() {
for (int i = 0; i < 1000; i++) {
ActivityControl c = new ActivityControl(); ====> I need to call this in a different thread but causes Invalid Cross Thread Exception
c.LoadSubControls();
}
}
}
Is there any way to create multiple UI Threads in order to avoid invalid cross thread exception?
I need multithreading for performance reasons and because the method call is very slow and the UI freezes.
Is there any way to call method SetSyncronizationContext (which is [SecurityCritical]) in Silverlight?
There is no avoiding creating these controls on the UI thread, but you could take advantage of System.Threading.Tasks.Task from Task Parallel Library (TPL) to allow for asynchronous operations.
I've been able to do something like this in silverlight 5 with a structure like this. Got the original idea looking at the source for Caliburn.Micro.
The following is a subset that applies to what you want.
public interface IPlatformProvider {
/// <summary>
/// Executes the action on the UI thread asynchronously.
/// </summary>
/// <param name = "action">The action to execute.</param>
System.Threading.Tasks.Task OnUIThreadAsync(Action action);
}
Here is the implementation.
/// <summary>
/// A <see cref="IPlatformProvider"/> implementation for the XAML platfrom (Silverlight).
/// </summary>
public class XamlPlatformProvider : IPlatformProvider {
private Dispatcher dispatcher;
public XamlPlatformProvider() {
dispatcher = System.Windows.Deployment.Current.Dispatcher;
}
private void validateDispatcher() {
if (dispatcher == null)
throw new InvalidOperationException("Not initialized with dispatcher.");
}
/// <summary>
/// Executes the action on the UI thread asynchronously.
/// </summary>
/// <param name = "action">The action to execute.</param>
public Task OnUIThreadAsync(System.Action action) {
validateDispatcher();
var taskSource = new TaskCompletionSource<object>();
System.Action method = () => {
try {
action();
taskSource.SetResult(null);
} catch (Exception ex) {
taskSource.SetException(ex);
}
};
dispatcher.BeginInvoke(method);
return taskSource.Task;
}
}
You could either go down the constructor DI route to pass in the provider or use a static locator pattern like this.
/// <summary>
/// Access the current <see cref="IPlatformProvider"/>.
/// </summary>
public static class PlatformProvider {
private static IPlatformProvider current = new XamlPlatformProvider();
/// <summary>
/// Gets or sets the current <see cref="IPlatformProvider"/>.
/// </summary>
public static IPlatformProvider Current {
get { return current; }
set { current = value; }
}
}
Now you should be able to make your calls without blocking the main thread and freezing the UI
public class BasicContainer : UserControl {
public async Task CreateMultipleActivityControls() {
var platform = PlatformProvider.Current;
for (var i = 0; i < 1000; i++) {
await platform.OnUIThreadAsync(() => {
var c = new ActivityControl();
c.LoadSubControls();
});
}
}
}
if making multiple calls to the dispatcher caused any performance issues you could instead move the entire process to one acync call.
public class BasicContainer : UserControl {
public async Task CreateMultipleActivityControls() {
var platform = PlatformProvider.Current;
await platform.OnUIThreadAsync(() => {
for (var i = 0; i < 1000; i++) {
var c = new ActivityControl();
c.LoadSubControls();
}
});
}
}
Related
As of now, the following code calls the Tick?.Invoke method every second. Is there a way to easily change the code so that it gets called every 5 seconds for example?
private void Timer()
{
var now = DateTime.Now;
int lastSecond = now.Second;
int lastDay = now.Day;
while (goOnTiming)
{
now = DateTime.Now;
if (now.Second != lastSecond)
{
Tick?.Invoke(this, new TickEventArgs(now: now));
lastSecond = now.Second;
}
if (lastDay != now.Day)
{
NewDay?.Invoke(this, new TickEventArgs(now: now));
lastDay = now.Day;
}
}
}
You could use a builtin timer class, there are various.
You could also use my base class for an async ticker:
/// <summary>
/// Base class for an async ticker (runs a function every period or 'tick')
/// Adapted from <see href="https://stackoverflow.com/a/62724908/4122889"/>
/// NOTE this class was not designed to handle UI in any way - it could, but test beforehand!
/// </summary>
public abstract class TickerBase
{
#region Properties and Fields
private readonly ILogger<TickerBase> _logger;
public TimeSpan Interval { get; protected set; }
private readonly Action<ILogger, string, Exception> _tickerStarted = LoggerMessage.Define<string>(LogLevel.Trace, new EventId(0, nameof(_tickerStarted)), "Starting ticker: {0}");
private readonly Action<ILogger, string, Exception> _tickerCancelled = LoggerMessage.Define<string>(LogLevel.Trace, new EventId(0, nameof(_tickerCancelled)), "Cancellation requested: {0}");
#endregion
#region Construction and Destruction
protected TickerBase(ILoggerFactory loggerFactory, TimeSpan interval)
{
if(interval == default) throw new ArgumentOutOfRangeException(nameof(interval), "Interval was its default value");
_logger = loggerFactory.CreateLogger<TickerBase>() ?? throw new ArgumentNullException(nameof(loggerFactory));
Interval = interval;
}
#endregion
#region Starting
/// <summary>
/// Start the ticker. Don't await this function, it will run indefinitely.
/// Be mindful of exceptions.
/// Calling this function more than once will start more tasks and thus more tickers.
/// </summary>
/// <param name="ctx">cancellation token to cancel the timer</param>
/// <returns></returns>
public async Task StartAsync(CancellationToken ctx = default)
{
// Log starting and stopping of ticker(s)
_tickerStarted(_logger, GetType().Name, null);
ctx.Register(() => _tickerCancelled(_logger, GetType().Name, null));
while (true)
{
var delayTask = Task.Delay(Interval, ctx);
await OnTickAsync(ctx)
.ConfigureAwait(false);
await delayTask
.ConfigureAwait(false);
ctx.ThrowIfCancellationRequested();
}
// ReSharper disable once FunctionNeverReturns Reason: as designed
}
protected abstract Task OnTickAsync(CancellationToken ctx = default);
}
#endregion
}
Use timers, you can use system timers or form timers. Below an example of a system timer:
private void CreateTimer()
{
var timer = new System.Threading.Timer(timerCallback);
timer.Change(0, 5000);
}
private void timerCallback(object state)
{
System.Console.WriteLine("The timer called me");
}
BTW probably you take a lot of CPU with your approach.
I have this code.
<ParentComponent>
<ChildComponet>
#renderFragment
</ChildComponent>
<ChildComponetn>
<GridComponent Data="#dataList"/>
</ChildComponent>
</ParentComponent>
where #renderFragment is dynamically render componet and Grid componet is list of some data with actions like "add new", "edit record", "delete".
If we click "add new", form for add new record is opened dynamically in #renderFragment and we want to refresh grid data after submit form but we don't know how to share some data between two child components. Same is about edit form, when some record is edited, we need to refresh grid component to show edited data.
If need more code and data about it please comment.
You may define a class service that implements the State pattern and the Notifier pattern to handle the state of your objects, pass state to objects, and notify subscriber objects of changes.
Here's a simplified example of such service, which enables a parent component to communicate with his children.
NotifierService.cs
public class NotifierService
{
private readonly List<string> values = new List<string>();
public IReadOnlyList<string> ValuesList => values;
public NotifierService()
{
}
public async Task AddTolist(string value)
{
values.Add(value);
await Notify?.Invoke();
}
public event Func<Task> Notify;
}
Child1.razor
#inject NotifierService Notifier
#implements IDisposable
<div>User puts in something</div>
<input type="text" #bind="#value" />
<button #onclick="#AddValue">Add value</button>
#foreach (var value in Notifier.ValuesList)
{
<p>#value</p>
}
#code {
private string value { get; set; }
public async Task AddValue()
{
await Notifier.AddTolist(value);
}
public async Task OnNotify()
{
await InvokeAsync(() =>
{
StateHasChanged();
});
}
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
Child2.razor
#inject NotifierService Notifier
<div>Displays Value from service and lets user put in new value</div>
<input type="text" #bind="#value" />
<button #onclick="#AddValue">Set Value</button>
#code {
private string value { get; set; }
public async Task AddValue()
{
await Notifier.AddTolist(value);
}
}
Usage
#page "/"
<p>
<Child1></Child1>
</p>
<p></p>
<p>
<Child2></Child2>
</p>
Startup.ConfigureServices
services.AddScoped<NotifierService>();
Hope this helps...
There are a few ways to do it, I just learned a really cool way using a Singleton class.
I have this component I use to send a message to other users in my chat called SubscriptionService, but you can use any class.
Add this inject to both of your components:
#inject Services.SubscriberService SubscriberService
#region using statements
using DataJuggler.UltimateHelper.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
#endregion
namespace BlazorChat.Services
{
#region class SubscriberService
/// <summary>
/// This class is used to subscribe to services, so other windows get a notification a new message
/// came in.
/// </summary>
public class SubscriberService
{
#region Private Variables
private int count;
private Guid serverId;
private List<SubscriberCallback> subscribers;
#endregion
#region Constructor
/// <summary>
/// Create a new instance of a 'SubscriberService' object.
/// </summary>
public SubscriberService()
{
// Create a new Guid
this.ServerId = Guid.NewGuid();
Subscribers = new List<SubscriberCallback>();
}
#endregion
#region Methods
#region BroadcastMessage(SubscriberMessage message)
/// <summary>
/// This method Broadcasts a Message to everyone that ins't blocked.
/// Note To Self: Add Blocked Feature
/// </summary>
public void BroadcastMessage(SubscriberMessage message)
{
// if the value for HasSubscribers is true
if ((HasSubscribers) && (NullHelper.Exists(message)))
{
// Iterate the collection of SubscriberCallback objects
foreach (SubscriberCallback subscriber in Subscribers)
{
// if the Callback exists
if ((subscriber.HasCallback) && (subscriber.Id != message.FromId))
{
// to do: Add if not blocked
// send the message
subscriber.Callback(message);
}
}
}
}
#endregion
#region GetSubscriberNames()
/// <summary>
/// This method returns a list of Subscriber Names ()
/// </summary>
public List<string> GetSubscriberNames()
{
// initial value
List<string> subscriberNames = null;
// if the value for HasSubscribers is true
if (HasSubscribers)
{
// create the return value
subscriberNames = new List<string>();
// Get the SubscriberNamesl in alphabetical order
List<SubscriberCallback> sortedNames = Subscribers.OrderBy(x => x.Name).ToList();
// Iterate the collection of SubscriberService objects
foreach (SubscriberCallback subscriber in sortedNames)
{
// Add this name
subscriberNames.Add(subscriber.Name);
}
}
// return value
return subscriberNames;
}
#endregion
#region Subscribe(string subscriberName)
/// <summary>
/// method returns a message with their id
/// </summary>
public SubscriberMessage Subscribe(SubscriberCallback subscriber)
{
// initial value
SubscriberMessage message = null;
// If the subscriber object exists
if ((NullHelper.Exists(subscriber)) && (HasSubscribers))
{
// Add this item
Subscribers.Add(subscriber);
// return a test message for now
message = new SubscriberMessage();
// set the message return properties
message.FromName = "Subscriber Service";
message.FromId = ServerId;
message.ToName = subscriber.Name;
message.ToId = subscriber.Id;
message.Data = Subscribers.Count.ToString();
message.Text = "Subscribed";
}
// return value
return message;
}
#endregion
#region Unsubscribe(Guid id)
/// <summary>
/// This method Unsubscribe
/// </summary>
public void Unsubscribe(Guid id)
{
// if the value for HasSubscribers is true
if ((HasSubscribers) && (Subscribers.Count > 0))
{
// attempt to find this callback
SubscriberCallback callback = Subscribers.FirstOrDefault(x => x.Id == id);
// If the callback object exists
if (NullHelper.Exists(callback))
{
// Remove this item
Subscribers.Remove(callback);
// create a new message
SubscriberMessage message = new SubscriberMessage();
// set the message return properties
message.FromId = ServerId;
message.FromName = "Subscriber Service";
message.Text = callback.Name + " has left the conversation.";
message.ToId = Guid.Empty;
message.ToName = "Room";
// Broadcast the message to everyone
BroadcastMessage(message);
}
}
}
#endregion
#endregion
#region Properties
#region Count
/// <summary>
/// This property gets or sets the value for 'Count'.
/// </summary>
public int Count
{
get { return count; }
set { count = value; }
}
#endregion
#region HasSubscribers
/// <summary>
/// This property returns true if this object has a 'Subscribers'.
/// </summary>
public bool HasSubscribers
{
get
{
// initial value
bool hasSubscribers = (this.Subscribers != null);
// return value
return hasSubscribers;
}
}
#endregion
#region ServerId
/// <summary>
/// This property gets or sets the value for 'ServerId'.
/// </summary>
public Guid ServerId
{
get { return serverId; }
set { serverId = value; }
}
#endregion
#region Subscribers
/// <summary>
/// This property gets or sets the value for 'Subscribers'.
/// </summary>
public List<SubscriberCallback> Subscribers
{
get { return subscribers; }
set { subscribers = value; }
}
#endregion
#endregion
}
#endregion
}
For my chat application, I want it available to all instances, so in your configure services method of Startup.cs, add a Sington:
services.AddSingleton<SubscriberService>();
To make it only available to this browser instance:
services.AddScoped(SubscriberService);
Now from both components you can call a method or get to properties on your injected class:
SubscriptionService.GetSubscribers();
Or if you prefer interfaces, I wrote a blog post about that and I don't want to duplicate the text:
https://datajugglerblazor.blogspot.com/2020/01/how-to-use-interfaces-to-communicate.html
The inject way is pretty cool though, as your entire application can communicate with other user instances for chat.
I am working with code that currently does not use any dependency injection, and makes multiple service calls through a WCF client.
public class MyClass
{
public void Method()
{
try
{
ServiceClient client = new ServiceClient();
client.Operation1();
}
catch(Exception ex)
{
// Handle Exception
}
finally
{
client = null;
}
try
{
ServiceClient client = new ServiceClient();
client.Operation2();
}
catch(Exception ex)
{
// Handle Exception
}
finally
{
client = null;
}
}
}
My goal is to make this code unit-testable through the use of dependency injection. My first thought was to simply pass an instance of the service client to the class constructor. Then in my unit tests, I can create a mock client for testing purposes that does not make actual requests to the web service.
public class MyClass
{
IServiceClient client;
public MyClass(IServiceClient client)
{
this.client = client;
}
public void Method()
{
try
{
client.Operation1();
}
catch(Exception ex)
{
// Handle Exception
}
try
{
client.Operation2();
}
catch(Exception ex)
{
// Handle Exception
}
}
}
However, I realized that this changes the code in a way that affects its original behavior, based on the information from this question: Reuse a client class in WCF after it is faulted
In the original code, if the call to Operation1 fails and the client is put in a faulted state, a new instance of ServiceClient is created, and Operation2 will still be called.
In the updated code, if the call to Operation1 fails, the same client is reused to call Operation2, but this call will fail if the client is in a faulted state.
Is it possible to create a new instance of the client while keeping the dependency injection pattern? I realize that reflection can be used to instantiate a class from a string, but I feel like reflection isn't the right way to go about this.
You need to inject factory rather than instance itself:
public class ServiceClientFactory : IServiceClientFactory
{
public IServiceClient CreateInstance()
{
return new ServiceClient();
}
}
Then in MyClass you simply use factory to get instance each time it is required:
// Injection
public MyClass(IServiceClientFactory serviceClientFactory)
{
this.serviceClientFactory = serviceClientFactory;
}
// Usage
try
{
var client = serviceClientFactory.CreateInstance();
client.Operation1();
}
Alternatively, you can inject function returning such client using Func<IServiceClient> delegate so that you can avoid creating extra class and interface:
// Injection
public MyClass(Func<IServiceClient> createServiceClient)
{
this.createServiceClient = createServiceClient;
}
// Usage
try
{
var client = createServiceClient();
client.Operation1();
}
// Instance creation
var myClass = new MyClass(() => new ServiceClient());
In your case Func<IServiceClient> should be sufficient. Once instance creation logic gets more complicated it would be a time reconsider explicitly implemented factory.
What I did in the past was have a generic client (with 'interception' using Unity) which creates a new connection from the ChannelFactory, based on the service's business interface, for each call and close that connection after each call, deciding on whether to indicate the connection is faulted based on whether an exception or normal response was returned. (See below.)
My real code using this client just requests an instance implementing the business interface and it will get an instance of this generic wrapper. The instance returned does not need to be disposed of, or treated differently based on whether an exception was returned. To get a service client (using the wrapper below) my code does: var client = SoapClientInterceptorBehavior<T>.CreateInstance(new ChannelFactory<T>("*")), which is usually hidden in a registry or passed in as a constructor argument. So in your case I would end up with var myClass = new MyClass(SoapClientInterceptorBehavior<IServiceClient>.CreateInstance(new ChannelFactory<IServiceClient>("*"))); (you probably want to put the whole call to create the instance in some factory method of your own, just requiring IServiceClient as input type, to make it a bit more readable. ;-))
In my tests I can just injected a mocked implementation of the service and test whether the right business methods were called and their results properly handled.
/// <summary>
/// IInterceptionBehavior that will request a new channel from a ChannelFactory for each call,
/// and close (or abort) it after each call.
/// </summary>
/// <typeparam name="T">business interface of SOAP service to call</typeparam>
public class SoapClientInterceptorBehavior<T> : IInterceptionBehavior
{
// create a logger to include the interface name, so we can configure log level per interface
// Warn only logs exceptions (with arguments)
// Info can be enabled to get overview (and only arguments on exception),
// Debug always provides arguments and Trace also provides return value
private static readonly Logger Logger = LogManager.GetLogger(LoggerName());
private static string LoggerName()
{
string baseName = MethodBase.GetCurrentMethod().DeclaringType.FullName;
baseName = baseName.Remove(baseName.IndexOf('`'));
return baseName + "." + typeof(T).Name;
}
private readonly Func<T> _clientCreator;
/// <summary>
/// Creates new, using channelFactory.CreatChannel to create a channel to the SOAP service.
/// </summary>
/// <param name="channelFactory">channelfactory to obtain connections from</param>
public SoapClientInterceptorBehavior(ChannelFactory<T> channelFactory)
: this(channelFactory.CreateChannel)
{
}
/// <summary>
/// Creates new, using the supplied method to obtain a connection per call.
/// </summary>
/// <param name="clientCreationFunc">delegate to obtain client connection from</param>
public SoapClientInterceptorBehavior(Func<T> clientCreationFunc)
{
_clientCreator = clientCreationFunc;
}
/// <summary>
/// Intercepts calls to SOAP service, ensuring proper creation and closing of communication
/// channel.
/// </summary>
/// <param name="input">invocation being intercepted.</param>
/// <param name="getNext">next interceptor in chain (will not be called)</param>
/// <returns>result from SOAP call</returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Logger.Info(() => "Invoking method: " + input.MethodBase.Name + "()");
// we will not invoke an actual target, or call next interception behaviors, instead we will
// create a new client, call it, close it if it is a channel, and return its
// return value.
T client = _clientCreator.Invoke();
Logger.Trace(() => "Created client");
var channel = client as IClientChannel;
IMethodReturn result;
int size = input.Arguments.Count;
var args = new object[size];
for(int i = 0; i < size; i++)
{
args[i] = input.Arguments[i];
}
Logger.Trace(() => "Arguments: " + string.Join(", ", args));
try
{
object val = input.MethodBase.Invoke(client, args);
if (Logger.IsTraceEnabled)
{
Logger.Trace(() => "Completed " + input.MethodBase.Name + "(" + string.Join(", ", args) + ") return-value: " + val);
}
else if (Logger.IsDebugEnabled)
{
Logger.Debug(() => "Completed " + input.MethodBase.Name + "(" + string.Join(", ", args) + ")");
}
else
{
Logger.Info(() => "Completed " + input.MethodBase.Name + "()");
}
result = input.CreateMethodReturn(val, args);
if (channel != null)
{
Logger.Trace("Closing channel");
channel.Close();
}
}
catch (TargetInvocationException tie)
{
// remove extra layer of exception added by reflective usage
result = HandleException(input, args, tie.InnerException, channel);
}
catch (Exception e)
{
result = HandleException(input, args, e, channel);
}
return result;
}
private static IMethodReturn HandleException(IMethodInvocation input, object[] args, Exception e, IClientChannel channel)
{
if (Logger.IsWarnEnabled)
{
// we log at Warn, caller might handle this without need to log
string msg = string.Format("Exception from " + input.MethodBase.Name + "(" + string.Join(", ", args) + ")");
Logger.Warn(msg, e);
}
IMethodReturn result = input.CreateExceptionMethodReturn(e);
if (channel != null)
{
Logger.Trace("Aborting channel");
channel.Abort();
}
return result;
}
/// <summary>
/// Returns the interfaces required by the behavior for the objects it intercepts.
/// </summary>
/// <returns>
/// The required interfaces.
/// </returns>
public IEnumerable<Type> GetRequiredInterfaces()
{
return new [] { typeof(T) };
}
/// <summary>
/// Returns a flag indicating if this behavior will actually do anything when invoked.
/// </summary>
/// <remarks>
/// This is used to optimize interception. If the behaviors won't actually
/// do anything (for example, PIAB where no policies match) then the interception
/// mechanism can be skipped completely.
/// </remarks>
public bool WillExecute
{
get { return true; }
}
/// <summary>
/// Creates new client, that will obtain a fresh connection before each call
/// and closes the channel after each call.
/// </summary>
/// <param name="factory">channel factory to connect to service</param>
/// <returns>instance which will have SoapClientInterceptorBehavior applied</returns>
public static T CreateInstance(ChannelFactory<T> factory)
{
IInterceptionBehavior behavior = new SoapClientInterceptorBehavior<T>(factory);
return (T)Intercept.ThroughProxy<IMy>(
new MyClass(),
new InterfaceInterceptor(),
new[] { behavior });
}
/// <summary>
/// Dummy class to use as target (which will never be called, as this behavior will not delegate to it).
/// Unity Interception does not allow ONLY interceptor, it needs a target instance
/// which must implement at least one public interface.
/// </summary>
public class MyClass : IMy
{
}
/// <summary>
/// Public interface for dummy target.
/// Unity Interception does not allow ONLY interceptor, it needs a target instance
/// which must implement at least one public interface.
/// </summary>
public interface IMy
{
}
}
We have an application that regularly receives multimedia messages, and should reply to them.
We currently do this with a single thread, first receiving messages, and then processing them one by one. This does the job, but is slow.
So we're now thinking of doing the same process but with multiple threads sumultaneously.
Any simple way to allow parallel processing of the incoming records, yet avoid erroneously processing the same record by two threads?
Any simple way to allow parallel processing of the incoming records, yet avoid erroneously processing the same record by two threads?
Yes it is actually not too hard, what you are wanting to do is called the "Producer-Consumer model"
If your message receiver could only handle one thread at a time but your message "processor" can work on multiple messages at once you just need to use a BlockingCollection to store the work that needs to be processed
public sealed class MessageProcessor : IDisposable
{
public MessageProcessor()
: this(-1)
{
}
public MessageProcessor(int maxThreadsForProcessing)
{
_maxThreadsForProcessing = maxThreadsForProcessing;
_messages = new BlockingCollection<Message>();
_cts = new CancellationTokenSource();
_messageProcessorThread = new Thread(ProcessMessages);
_messageProcessorThread.IsBackground = true;
_messageProcessorThread.Name = "Message Processor Thread";
_messageProcessorThread.Start();
}
public int MaxThreadsForProcessing
{
get { return _maxThreadsForProcessing; }
}
private readonly BlockingCollection<Message> _messages;
private readonly CancellationTokenSource _cts;
private readonly Thread _messageProcessorThread;
private bool _disposed = false;
private readonly int _maxThreadsForProcessing;
/// <summary>
/// Add a new message to be queued up and processed in the background.
/// </summary>
public void ReceiveMessage(Message message)
{
_messages.Add(message);
}
/// <summary>
/// Signals the system to stop processing messages.
/// </summary>
/// <param name="finishQueue">Should the queue of messages waiting to be processed be allowed to finish</param>
public void Stop(bool finishQueue)
{
_messages.CompleteAdding();
if(!finishQueue)
_cts.Cancel();
//Wait for the message processor thread to finish it's work.
_messageProcessorThread.Join();
}
/// <summary>
/// The background thread that processes messages in the system
/// </summary>
private void ProcessMessages()
{
try
{
Parallel.ForEach(_messages.GetConsumingEnumerable(),
new ParallelOptions()
{
CancellationToken = _cts.Token,
MaxDegreeOfParallelism = MaxThreadsForProcessing
},
ProcessMessage);
}
catch (OperationCanceledException)
{
//Don't care that it happened, just don't want it to bubble up as a unhandeled exception.
}
}
private void ProcessMessage(Message message, ParallelLoopState loopState)
{
//Here be dragons! (or your code to process a message, your choice :-))
//Use if(_cts.Token.IsCancellationRequested || loopState.ShouldExitCurrentIteration) to test if
// we should quit out of the function early for a graceful shutdown.
}
public void Dispose()
{
if(!_disposed)
{
if(_cts != null && _messages != null && _messageProcessorThread != null)
Stop(true); //This line will block till all queued messages have been processed, if you want it to be quicker you need to call `Stop(false)` before you dispose the object.
if(_cts != null)
_cts.Dispose();
if(_messages != null)
_messages.Dispose();
GC.SuppressFinalize(this);
_disposed = true;
}
}
~MessageProcessor()
{
//Nothing to do, just making FXCop happy.
}
}
I highly recommend you read the free book Patterns for Parallel Programming, it goes in to some detail about this. There is a entire section explaining the Producer-Consumer model in detail.
UPDATE: There are some performance issues with GetConsumingEnumerable() and Parallel.ForEach(, instead use the library ParallelExtensionsExtras and it's new extension method GetConsumingPartitioner()
public static Partitioner<T> GetConsumingPartitioner<T>(
this BlockingCollection<T> collection)
{
return new BlockingCollectionPartitioner<T>(collection);
}
private class BlockingCollectionPartitioner<T> : Partitioner<T>
{
private BlockingCollection<T> _collection;
internal BlockingCollectionPartitioner(
BlockingCollection<T> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
_collection = collection;
}
public override bool SupportsDynamicPartitions {
get { return true; }
}
public override IList<IEnumerator<T>> GetPartitions(
int partitionCount)
{
if (partitionCount < 1)
throw new ArgumentOutOfRangeException("partitionCount");
var dynamicPartitioner = GetDynamicPartitions();
return Enumerable.Range(0, partitionCount).Select(_ =>
dynamicPartitioner.GetEnumerator()).ToArray();
}
public override IEnumerable<T> GetDynamicPartitions()
{
return _collection.GetConsumingEnumerable();
}
}
Consider the sample code below consisting of a Class Library design and an executable Program using the library.
namespace AppLib
{
/// <summary>
/// Entry point for library. Stage manages all the actors in the logic.
/// </summary>
class StageApp
{
/// <summary>
/// Setting that is looked up by different actors
/// </summary>
public int SharedSetting { get; set; }
/// <summary>
/// Stage managing actors with app logic
/// </summary>
public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
private List<Actor> m_actors = new List<Actor>();
}
/// <summary>
/// An object on the stage. Refers to stage (shared)settings and execute depending on the settings.
/// Hence actor should have reference to stage
/// </summary>
class Actor
{
private StageApp m_StageApp;
private int m_Property;
/// <summary>
/// An actor that needs to refer to stage to know what behavior to execute
/// </summary>
/// <param name="stage"></param>
public Actor(StageApp stage)
{
m_StageApp = stage;
m_Property = new Random().Next();
}
/// <summary>
/// Execute according to stage settings
/// </summary>
/// <returns></returns>
public int Execute()
{
return m_StageApp.SharedSetting * m_Property;
}
}
}
namespace AppExe
{
using AppLib;
class Program
{
static void Main(string[] args)
{
StageApp app = new StageApp();
app.SharedSetting = 5;
// Question: How to add actor to stage?
foreach (var actor in app.Actors)
Console.WriteLine(actor.Execute());
}
}
}
Question
Stage and Actor have circular dependency and seems bad to me.
For example, how should we add actors to stage?
If I let user to create new Actor() themselves,
then they must keep on supplying the Stage.
If I give Actor() an internal constructor and make Stage a factory,
then I lose some of the flexibility for users to do making inherited Actors.
If I make Stage a singleton, then I can only have one set of SharedSetting.
In case the user wants more than one Stage in his AppExe, then it cannot be done.
Is there anyway to redesign the architecture so as to avoid the problems above?
If your functionality is not limited by sharing the StageApp settings between actors, but also will be some other logic. For example when you need to know parent StageApp from Actor and vice versa. I preffer to implement it in this way:
namespace AppLib
{
/// <summary>
/// Entry point for library. Stage manages all the actors in the logic.
/// </summary>
class StageApp
{
/// <summary>
/// Setting that is looked up by different actors
/// </summary>
public int SharedSetting { get; set; }
/// <summary>
/// Stage managing actors with app logic
/// </summary>
public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
private List<Actor> m_actors = new List<Actor>();
public int TotalActorsCount
{
get
{
return m_actors.Count;
}
}
public void AddActor(Actor actor)
{
if (actor == null)
throw new ArgumentNullException("actor");
if (m_actors.Contains(actor))
return; // or throw an exception
m_actors.Add(actor);
if (actor.Stage != this)
{
actor.Stage = this;
}
}
// we are hiding this method, to avoid because we can change Stage only to another non null value
// so calling this method directly is not allowed
internal void RemoveActor(Actor actor)
{
if (actor == null)
throw new ArgumentNullException("actor");
if (!m_actors.Contains(actor))
return; // or throuw exception
m_actors.Remove(actor);
}
}
/// <summary>
/// An object on the stage. Refers to stage (shared)settings and execute depending on the settings.
/// Hence actor should have reference to stage
/// </summary>
class Actor
{
private StageApp m_StageApp;
private int m_Property;
public StageApp Stage
{
get
{
return m_StageApp;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (m_StageApp != value)
{
if (m_StageApp != null) // not a call from ctor
{
m_StageApp.RemoveActor(this);
}
m_StageApp = value;
m_StageApp.AddActor(this);
}
}
}
/// <summary>
/// An actor that needs to refer to stage to know what behavior to execute
/// </summary>
/// <param name="stage"></param>
public Actor(StageApp stage)
{
Stage = stage;
m_Property = new Random().Next();
}
/// <summary>
/// Execute according to stage settings
/// </summary>
/// <returns></returns>
public int Execute()
{
return m_StageApp.SharedSetting * m_Property;
}
}
}
namespace AppExe
{
using AppLib;
class Program
{
static void Main(string[] args)
{
StageApp app = new StageApp();
app.SharedSetting = 5;
StageApp anotherApp = new StageApp();
anotherApp.SharedSetting = 6;
// actor is added to the stage automatically after instantiation
Actor a1 = new Actor(app);
Actor a2 = new Actor(app);
Actor a3 = new Actor(anotherApp);
Console.WriteLine("Actors in anotherApp before moving actor:");
Console.WriteLine(anotherApp.TotalActorsCount);
// or by calling method from StageApp class
anotherApp.AddActor(a1);
Console.WriteLine("Actors in anotherApp after calling method (should be 2):");
Console.WriteLine(anotherApp.TotalActorsCount);
// or by setting Stage through property
a2.Stage = anotherApp;
Console.WriteLine("Actors in anotherApp after setting property of Actor instance (should be 3):");
Console.WriteLine(anotherApp.TotalActorsCount);
Console.WriteLine("Actors count in app (should be empty):");
Console.WriteLine(app.TotalActorsCount);
}
}
}
It allows to you to manipulate with object relationships transparently, but requires a little bit mor code to implement.
How about adding a new class "ActorRole" that defines the behaviour of the actor in each Stage. It lets you decouple Actor and Stage from each other, so you can instantiate both independently (through a factory for example) and then combine them creating ActorRole objects that configure your stages. This combinations can be made using a Builder pattern if it is needed.
If you need to dynamically change your actor behaviour, you can use a Strategy pattern based on the ActorRole class, so depending on the Stage, you can assign to the actor different concrete implementations of its behaviour.
I would solve it by using Func instead of passing in the Stage to the Actor. Like this:
namespace AppExe
{
using AppLib;
class Program
{
static void Main(string[] args)
{
StageApp app = new StageApp();
app.CreateActor();
app.SharedSetting = 5;
foreach (var actor in app.Actors)
Console.WriteLine(actor.Execute());
}
}
}
namespace AppLib
{
class StageApp
{
public int SharedSetting { get; set; }
public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
private List<Actor> m_actors = new List<Actor>();
public void CreateActor()
{
m_actors.Add(new Actor(Executed));
}
private int Executed(int arg)
{
return SharedSetting * arg;
}
}
class Actor
{
private int m_Property;
private Func<int, int> m_executed;
public Actor(Func<int, int> executed)
{
m_executed = executed;
m_Property = new Random().Next();
}
public int Execute()
{
return m_executed(m_Property);
}
}
}
I totally agree with you that circular references is not fun :).
You could also solve this using events, but I like passing functions like callback.