I have a class
public class ProductionQueue
{
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>
/// The title.
/// </value>
public string? Name { get; set; }
/// <summary>
/// Maximum capacity of the queue. Keep it null for inifinte capacity.
/// </summary>
public TimeSpan? Capacity { get; set; }
/// <summary>
/// Gets or sets the tasks.
/// </summary>
/// <value>
/// The tasks.
/// </value>
public IList<Task> Tasks { get; set; } = new List<Task>();
}
Let me just explain that the Task object also contains a variable (probably another Timespan) that contains the time to perform the task.
Is it possible to add an event, an observer or any other behaviour in my ProductionQueue to throw an exception if my Capacity is reached when I add a new task in my Task list?
I have written this simple solution based on your initial code but with using an ObservableCollection, so the capacity can be check every time the collection is modified.
It could be done in more effecient way using the data provided by NotifyCollectionChangedEventArgs (type of modification, NewItems, OldItems etc.) to recompute only what's changed, instead of iterating through the whole collection every time.
// simple object for the sake of this example
public class Task
{
public TimeSpan TimeToPerform;
}
public class ProductionQueue
{
public string? Name { get; set; }
/// <summary>
/// Maximum capacity of the queue. Keep it null for inifinte capacity.
/// </summary>
public TimeSpan? Capacity { get; set;}
public ObservableCollection<Task> Tasks = new ObservableCollection <Task>();
// ProductionQueue ctor
public ProductionQueue(TimeSpan? initialCapacity)
{
this.Capacity = initialCapacity;
// subscribe to know when the collection gets changed
this.Tasks.CollectionChanged += (s, e) => {
Console.WriteLine("collection changed");
this.checkCapacity();
};
}
private void checkCapacity()
{
var totalTime = TimeSpan.Zero;
foreach (var task in this.Tasks)
{
totalTime+= task.TimeToPerform;
}
if (totalTime > this.Capacity)
throw new Exception("queue time capacity exceeded");
}
}
}
And here is an example of program using it:
public static void Main(string[] args)
{
try
{
var PQ = new ProductionQueue(TimeSpan.FromHours(1));
PQ.Tasks.Add(new Task(){ TimeToPerform=TimeSpan.FromMinutes(40)});
PQ.Tasks.Add(new Task(){ TimeToPerform=TimeSpan.FromMinutes(30)});
Console.WriteLine("Tasks added without problem");
}
catch(Exception e)
{
Console.WriteLine("Exception occured: "+e.Message);
}
}
Console output:
collection changed
collection changed
Exception occured: queue time capacity exceeded
Related
I'm new to the C# world and I'm trying to create my first project.
I want to create application that read Ant+ heart rate sensors and analyze the data received.
I've created 2 classes
public class Athlete
{
public string Name { get; set; }
public string Surname { get; set; }
public int HBrate { get; set; }
public int HBcount { get; set; }
}
}
public class AntSensor
{
public string Name { get; set; }
public string SensorID { get; set; }
public byte ChannelNr { get; set; }
}
}
After selecting the Athlete and the sensor from a UI I initialize the Heart Rate sensor and open the receiver channel
public void AthleteStart()
{
if ((AthleteSelected != null) || (ANTSensorSelected != null))
{
AthleteLabelTxt = $"{AthleteSelected.Name}";
ANT_Device device0 = new ANT_Device();
ANT_Channel channel0 = device0.getChannel(0x00);
HRProject.AntApps(AthleteSelected, device0, channel0, new string[0]);
}
}
then after some code that control the communication protocol I have:
static void DeviceResponse(ANT_Response response)
{
// some code to control the response
// and the a call to Update my sensor data
SensorUpdate(response.getDataPayload());
}
how I can put here some code that update HBrate or HBcount on my AthleteSelected or ANTSensorSelected?
As Tim Schmelter already commented: The given information are not that much useful to find out at which point you are failing.
But to try to answer your question in a more general manner:
Updating a class (but you mean a class-instance) property from a static method, requires to give the class instance as a parameter to that method.
public class MyInstanceClass
{
public string MyValue { get; set; }
}
public static class MyStaticClass
{
public static void DoSomething(MyInstanceClass instance)
{
instance.MyValue = "New Value";
}
}
I don't use ANTSensorSelected because I was thinking to update directly AthleteSelected but if need I can eventually add.
for the communication I'm using the ANT_Managed_Library from Garmin here the ANT_Response class:
public class ANT_Response
{
/// <summary>
/// The object that created this response (ie: The corresponding ANTChannel or ANTDevice instance).
/// </summary>
public object sender;
/// <summary>
/// The channel parameter received in the message. Note: For some messages this is not applicable.
/// </summary>
public byte antChannel;
/// <summary>
/// The time the message was received.
/// </summary>
public DateTime timeReceived;
/// <summary>
/// The MessageID of the response
/// </summary>
public byte responseID;
/// <summary>
/// The raw contents of the response message
/// </summary>
public byte[] messageContents;
internal ANT_Response(object sender, byte antChannel, DateTime timeReceived, byte IDcode, byte[] messageContents)
{
this.sender = sender;
this.antChannel = antChannel;
this.timeReceived = timeReceived;
this.responseID = IDcode;
this.messageContents = messageContents;
}
The code it's quite long let me try to add the most significant parts:
channel0.channelResponse += new dChannelResponseHandler(ChannelResponse); // Add channel response function to receive channel event messages
/// Delegate for Channel Response Event
/// </summary>
/// <param name="response">Message details received from device</param>
public delegate void dChannelResponseHandler(byte antChannel, ANT_Response response);
here the ChannelResponse
static void ChannelResponse(byte antChannel, ANT_Response response)
{
try
{
switch ((ANT_ReferenceLibrary.ANTMessageID)response.responseID)
{
case ANT_ReferenceLibrary.ANTMessageID.RESPONSE_EVENT_0x40:
//some extra code with more cases
SensorUpdate(response.getDataPayload());
here the getDataPayLoad
/// Returns the 8-byte data payload of an ANT message. Throws an exception if this is not a received message.
/// </summary>
/// <returns></returns>
public byte[] getDataPayload()
{
if (messageContents.Length == 9
&& (responseID == (byte)ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E
|| responseID == (byte)ANT_ReferenceLibrary.ANTMessageID.ACKNOWLEDGED_DATA_0x4F
|| responseID == (byte)ANT_ReferenceLibrary.ANTMessageID.BURST_DATA_0x50
))
return messageContents.Skip(1).ToArray(); //Just skip the channel byte and return the payload
else
return splitExtMessage(extMsgParts.DataPayload); // Extended message
}
I have three questions:
What do you generally think about my approach to solve the given problem?
What do you think I could further improve performance wise?
The most important one: How do I make my implementation really thread safe?
At first the simplified scenario I'm in:
I am communicating via a messaging system with different devices. I am receiving and sending thousands and thousands of messages in a rather short time period. I am inside of a multithreading environment so a lot of different tasks are sending and expecting messages. For the message reception an event driven approach got us a lot of trouble in the sense of making it thread safe.
I have a few Receiver tasks which get messages from outside and have to deliver these messages to a lot of consumer tasks.
So I came up with a different approach:
Why not have a history of a few thousand messages where every new message is enqueued and the consumer tasks can search backwards from the newest item to the last processed item in order to get all newly arrived messages. Of course this has to be fast and thread safe.
I came up with the idea of a linked ring buffer and implemented the following:
public class LinkedRingBuffer<T>
{
private LinkedRingBufferNode<T> firstNode;
private LinkedRingBufferNode<T> lastNode;
public LinkedRingBuffer(int capacity)
{
Capacity = capacity;
Count = 0;
}
/// <summary>
/// Maximum count of items inside the buffer
/// </summary>
public int Capacity { get; }
/// <summary>
/// Actual count of items inside the buffer
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Get value of the oldest buffer entry
/// </summary>
/// <returns></returns>
public T GetFirst()
{
return firstNode.Item;
}
/// <summary>
/// Get value of the newest buffer entry
/// </summary>
/// <returns></returns>
public T GetLast()
{
return lastNode.Item;
}
/// <summary>
/// Add item at the end of the buffer.
/// If capacity is reached the link to the oldest item is deleted.
/// </summary>
public void Add(T item)
{
/* create node and set to last one */
var node = new LinkedRingBufferNode<T>(lastNode, item);
lastNode = node;
/* if it is the first node, the created is also the first */
if (firstNode == null)
firstNode = node;
/* check for capacity reach */
Count++;
if(Count > Capacity)
{/* deleted all links to the current first so that its eventually gc collected */
Count = Capacity;
firstNode = firstNode.NextNode;
firstNode.PreviousNode = null;
}
}
/// <summary>
/// Iterate through the buffer from the oldest to the newest item
/// </summary>
public IEnumerable<T> LastToFirst()
{
var current = lastNode;
while(current != null)
{
yield return current.Item;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to the oldest item
/// </summary>
public IEnumerable<T> FirstToLast()
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
current = current.NextNode;
}
}
/// <summary>
/// Iterate through the buffer from the oldest to given item.
/// If item doesn't exist it iterates until it reaches the newest
/// </summary>
public IEnumerable<T> LastToReference(T item)
{
var current = lastNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to given item.
/// If item doesn't exist it iterates until it reaches the oldest
/// </summary>
public IEnumerable<T> FirstToReference(T item)
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Represents a linked node inside the buffer and holds the data
/// </summary>
private class LinkedRingBufferNode<A>
{
public LinkedRingBufferNode(LinkedRingBufferNode<A> previousNode, A item)
{
Item = item;
NextNode = null;
PreviousNode = previousNode;
if(previousNode != null)
previousNode.NextNode = this;
}
internal A Item { get; }
internal LinkedRingBufferNode<A> PreviousNode { get; set; }
internal LinkedRingBufferNode<A> NextNode { get; private set; }
}
}
But unfortunately I'm kind of new to the multithreading environment, so how would I make this buffer thread safe for multiple reads and writes?
Thanks!
I think the simplest way would be to have a synchronization object which you would lock on, whenever performing thread-critical code. The code within a lock block is called the critical section, and can only be accessed by one thread at a time. Any other thread wishing to access it will wait, until the lock is released.
Definition and initialization:
private object Synchro;
public LinkedRingBuffer(int capacity)
{
Synchro = new object();
// Other constructor code
}
Usage:
public T GetFirst()
{
lock(Synchro)
{
return firstNode.Item;
}
}
When writing thread-safe code, locking some parts may seem obvious. But if you're not sure whether or not to lock a statement or block of code, for both read and write safety you need to consider:
Whether or not this code can influence the behavior or result of any other locked critical sections.
Whether or not any other locked critical sections can influence this code's behavior or result.
You will also need to rewrite some of your auto-implemented properties to have a backing field. It should be pretty straightforward, however...
Your usage of yield return, while being pretty smart and efficient in a single-thread context, will cause trouble in a multi-threaded context. This is because yield return doesn't release a lock statement (and it shouldn't). You will have to perform materialization in a wrapper, wherever you use yield return.
Your thread-safe code looks like this:
public class LinkedRingBuffer<T>
{
private LinkedRingBufferNode<T> firstNode;
private LinkedRingBufferNode<T> lastNode;
private object Synchro;
public LinkedRingBuffer(int capacity)
{
Synchro = new object();
Capacity = capacity;
Count = 0;
}
/// <summary>
/// Maximum count of items inside the buffer
/// </summary>
public int Capacity { get; }
/// <summary>
/// Actual count of items inside the buffer
/// </summary>
public int Count
{
get
{
lock (Synchro)
{
return _count;
}
}
private set
{
_count = value;
}
}
private int _count;
/// <summary>
/// Get value of the oldest buffer entry
/// </summary>
/// <returns></returns>
public T GetFirst()
{
lock (Synchro)
{
return firstNode.Item;
}
}
/// <summary>
/// Get value of the newest buffer entry
/// </summary>
/// <returns></returns>
public T GetLast()
{
lock (Synchro)
{
return lastNode.Item;
}
}
/// <summary>
/// Add item at the end of the buffer.
/// If capacity is reached the link to the oldest item is deleted.
/// </summary>
public void Add(T item)
{
lock (Synchro)
{
/* create node and set to last one */
var node = new LinkedRingBufferNode<T>(lastNode, item);
lastNode = node;
/* if it is the first node, the created is also the first */
if (firstNode == null)
firstNode = node;
/* check for capacity reach */
Count++;
if (Count > Capacity)
{
/* deleted all links to the current first so that its eventually gc collected */
Count = Capacity;
firstNode = firstNode.NextNode;
firstNode.PreviousNode = null;
}
}
}
/// <summary>
/// Iterate through the buffer from the oldest to the newest item
/// </summary>
public IEnumerable<T> LastToFirst()
{
lock (Synchro)
{
var materialized = LastToFirstInner().ToList();
return materialized;
}
}
private IEnumerable<T> LastToFirstInner()
{
var current = lastNode;
while (current != null)
{
yield return current.Item;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to the oldest item
/// </summary>
public IEnumerable<T> FirstToLast()
{
lock (Synchro)
{
var materialized = FirstToLastInner().ToList();
return materialized;
}
}
private IEnumerable<T> FirstToLastInner()
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
current = current.NextNode;
}
}
/// <summary>
/// Iterate through the buffer from the oldest to given item.
/// If item doesn't exist it iterates until it reaches the newest
/// </summary>
public IEnumerable<T> LastToReference(T item)
{
lock (Synchro)
{
var materialized = LastToReferenceInner(item).ToList();
return materialized;
}
}
private IEnumerable<T> LastToReferenceInner(T item)
{
var current = lastNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Iterate through the buffer from the newest to given item.
/// If item doesn't exist it iterates until it reaches the oldest
/// </summary>
public IEnumerable<T> FirstToReference(T item)
{
lock (Synchro)
{
var materialized = FirstToReferenceInner(item).ToList();
return materialized;
}
}
private IEnumerable<T> FirstToReferenceInner(T item)
{
var current = firstNode;
while (current != null)
{
yield return current.Item;
if (current.Item.Equals(item))
break;
current = current.PreviousNode;
}
}
/// <summary>
/// Represents a linked node inside the buffer and holds the data
/// </summary>
private class LinkedRingBufferNode<A>
{
public LinkedRingBufferNode(LinkedRingBufferNode<A> previousNode, A item)
{
Item = item;
NextNode = null;
PreviousNode = previousNode;
if (previousNode != null)
previousNode.NextNode = this;
}
internal A Item { get; }
internal LinkedRingBufferNode<A> PreviousNode { get; set; }
internal LinkedRingBufferNode<A> NextNode { get; private set; }
}
}
There can be some optimizations done, for example you don't need to create the LinkedRingBufferNode objects inside the critical section, however you would have to copy the lastNode value to a local variable inside a critical section, before creating the object.
Today my problem is in that i can't deal with the fileSystemWatcher (even with debug).
So... i want to use the function GetHashFromFile(string path, HashAlgorithm algorithm ) while the fileSystemWatcher is watching the choosen directory. Once it gets an change in this directory (File has been created, re-named, change..) i want to use the e.fullPath as 1st argument in GetHashFromFile, but it throws me an exception about that this file can't be found. Could someone tell me on wich place in the code should i use the GetHashFromFile() ?
Thanks !
Here is some sample code which I created for a different SO question which correctly uses FileSystemWatcher to process files which should meet your needs
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Security.Permissions;
namespace ConsoleApplication9
{
internal class Program
{
private static void Main(string[] args)
{
const string directorytowatch = #"d:\junk\watch\"; // the directory to watch for new files
// this initiates a filesystemmonitor to watch for new files being created
Task.Factory.StartNew(() => FileSystemMonitor.Instance.WatchDirectory(directorytowatch));
// initiate the processing of any new files
FilesWorker.Instance.ReadQueue();
Console.ReadLine();
}
}
/// <summary>
/// Monitors the filesystem in "real-time" to check for new files
/// </summary>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
internal class FileSystemMonitor : SingletonBase<FileSystemMonitor>
{
private FileSystemMonitor()
{
}
internal void WatchDirectory(string dir)
{
var watcher = new FileSystemWatcher(dir)
{
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.LastAccess,
Filter = "*.*"
};
// watch all files
watcher.Created += WatcherOnCreated;
watcher.EnableRaisingEvents = true;
}
private static void WatcherOnCreated(object sender, FileSystemEventArgs fileSystemEventArgs)
{
Console.WriteLine(fileSystemEventArgs.FullPath + "" + fileSystemEventArgs.ChangeType); // for test purposes
var fileInfo = new FileInfo(fileSystemEventArgs.FullPath);
FilesWorker.Instance.AddToQueue(fileInfo);
}
}
/// <summary>
/// handles the queue of files to be processed and the syncronisation of tasks related to the queue
/// </summary>
internal class FilesWorker : SingletonBase<FilesWorker>
{
private FilesWorker()
{
}
/// <summary>
/// The queue of files which still need to be processed
/// </summary>
private readonly ConcurrentQueue<FileInfo> _filesQueue = new ConcurrentQueue<FileInfo>();
/// <summary>
/// create a semaphore to limit the number of threads which can process a file at any given time
// In this case only allow 2 to be processed at any given time
/// </summary>
private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(2, 2);
/// <summary>
/// add new file to the queue
/// </summary>
/// <param name="fileInfo"></param>
internal void AddToQueue(FileInfo fileInfo)
{
_filesQueue.Enqueue(fileInfo);
}
/// <summary>
/// executes a method on a given timeframe
/// </summary>
/// <param name="method">method to execute</param>
/// <param name="timer">time between execution runs (seconds)</param>
internal void ExecuteMethod(Action method, double timer)
{
IObservable<long> observable = Observable.Interval(TimeSpan.FromSeconds(timer));
// Token for cancelation
var source = new CancellationTokenSource();
observable.Subscribe(x =>
{
var task = new Task(method);
task.Start();
}, source.Token);
}
/// <summary>
/// Get any new files and send for processing
/// </summary>
internal void ReadQueue()
{
// check the queue every two seconds
ExecuteMethod(ProcessQueue, 2d);
}
/// <summary>
/// takes files from the queue and starts processing
/// </summary>
internal void ProcessQueue()
{
try
{
Semaphore.Wait();
FileInfo fileInfo;
while (_filesQueue.TryDequeue(out fileInfo))
{
var fileProcessor = new FileProcessor();
fileProcessor.ProcessFile(fileInfo);
}
}
finally
{
Semaphore.Release();
}
}
}
internal class FileProcessor
{
internal void ProcessFile(FileInfo fileInfo)
{
// do some long running tasks with the file
}
}
/// <summary>
/// Implements singleton pattern on all classes which derive from it
/// </summary>
/// <typeparam name="T">Derived class</typeparam>
public abstract class SingletonBase<T> where T : class
{
public static T Instance
{
get { return SingletonFactory.Instance; }
}
/// <summary>
/// The singleton class factory to create the singleton instance.
/// </summary>
private class SingletonFactory
{
static SingletonFactory()
{
}
private SingletonFactory()
{
}
internal static readonly T Instance = GetInstance();
private static T GetInstance()
{
var theType = typeof(T);
T inst;
try
{
inst = (T)theType
.InvokeMember(theType.Name,
BindingFlags.CreateInstance | BindingFlags.Instance
| BindingFlags.NonPublic,
null, null, null,
CultureInfo.InvariantCulture);
}
catch (MissingMethodException ex)
{
var exception = new TypeLoadException(string.Format(
CultureInfo.CurrentCulture,
"The type '{0}' must have a private constructor to " +
"be used in the Singleton pattern.", theType.FullName)
, ex);
//LogManager.LogException(LogManager.EventIdInternal, exception, "error in instantiating the singleton");
throw exception;
}
return inst;
}
}
}
}
in my main WPF application code i need to run .exe app with command prompt. this action is executed inside backgroundworker i have the following code. the code is running readlines.exe app with command prompt and read the output lines into a string (str).
string str;
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Hidden;
proc.UseShellExecute = true;
proc.FileName = #"readlines.exe";
proc.Arguments = #"";
proc.UseShellExecute = false;
proc.RedirectStandardOutput = true;
proc.CreateNoWindow = true;
proc.RedirectStandardInput = true;
Process proc1 = Process.Start(proc);
proc1.StandardInput.WriteLine("");
str = proc1.StandardOutput.ReadToEnd();
i want to ad timeout to the below line so when the timeout will be finised the procces will be canceled (as CTR+C) and "str" will get the output text until this point.
str = proc1.StandardOutput.ReadToEnd();
is it possible?
Although the previous answer has already been accepted here is a maybe more useful, secure and performant solution. Further it does not make use of the ReadLine() method which would block until there has been one line written (which may never occur). It uses an instance of StringBuilder and reads from the stream in specifyable data blocks (default size is 128 characters). Furthermore it supports event based notification of read data.
The usage of the class stays the same.
ProcessOutputReader por = new ProcessOutputReader(proc1);
por.StartReading();
// Do whatever you want here
// (e.g. sleep or whatever)
por.StopReading();
// Now you have everything that has been read in por.Data
However I've added the OnDataRead event which is fired every time new data has been read. You can access the data by using e.g. following code:
...
// Subscribe to the event
por.OnDataRead += OnDataReadEventHandler;
...
The callback method / event handler would look something like this:
private void OnDataReadEventHandler(object sender, ProcessOutputReaderEventArgs e)
{
// e.IntermediateDataStore points to the StringBuilder instance which holds
// all the data that has been received until now.
string completeData = e.IntermediateDataStore.ToString();
// e.NewData points to a string which contains the data that has been received
// since the last triggered event (because the event is triggered on each read).
string newData = e.NewData;
}
The modified ProcessOutputReader class looks like this:
/// <summary>
/// Represents the ProcessOutputReader class.
/// </summary>
public class ProcessOutputReader
{
/// <summary>
/// Represents the instance of the thread arguments class.
/// </summary>
private ProcessOutputReaderWorkerThreadArguments threadArguments;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReader"/> class.
/// </summary>
/// <param name="process">The process which's output shall be read.</param>
/// <exception cref="System.ArgumentOutOfRangeException">Is thrown if the specified process reference is null.</exception>
public ProcessOutputReader(Process process)
{
if (process == null)
{
throw new ArgumentOutOfRangeException("process", "The parameter \"process\" must not be null");
}
this.Process = process;
this.IntermediateDataStore = new StringBuilder();
this.threadArguments = new ProcessOutputReaderWorkerThreadArguments(this.Process, this.IntermediateDataStore);
}
/// <summary>
/// Is fired whenever data has been read from the process output.
/// </summary>
public event EventHandler<ProcessOutputReaderEventArgs> OnDataRead;
/// <summary>
/// Gets or sets the worker thread.
/// </summary>
private Thread ReaderThread
{
get;
set;
}
/// <summary>
/// Gets or sets the intermediate data store.
/// </summary>
private StringBuilder IntermediateDataStore
{
get;
set;
}
/// <summary>
/// Gets the data collected from the process output.
/// </summary>
public string Data
{
get
{
return this.IntermediateDataStore.ToString();
}
}
/// <summary>
/// Gets the process.
/// </summary>
public Process Process
{
get;
private set;
}
/// <summary>
/// Stars reading from the process output.
/// </summary>
public void StartReading()
{
if (this.ReaderThread != null)
{
if (this.ReaderThread.IsAlive)
{
return;
}
}
this.ReaderThread = new Thread(new ParameterizedThreadStart(ReaderWorker));
this.threadArguments.Exit = false;
this.ReaderThread.Start(this.threadArguments);
}
/// <summary>
/// Stops reading from the process output.
/// </summary>
public void StopReading()
{
if (this.ReaderThread != null)
{
if (this.ReaderThread.IsAlive)
{
this.threadArguments.Exit = true;
this.ReaderThread.Join();
}
}
}
/// <summary>
/// Fires the OnDataRead event.
/// </summary>
/// <param name="newData">The new data that has been read.</param>
protected void FireOnDataRead(string newData)
{
if (this.OnDataRead != null)
{
this.OnDataRead(this, new ProcessOutputReaderEventArgs(this.IntermediateDataStore, newData));
}
}
/// <summary>
/// Represents the worker method.
/// </summary>
/// <param name="data">The thread arguments, must be an instance of the <see cref="ProcessOutputReaderWorkerThreadArguments"/> class.</param>
private void ReaderWorker(object data)
{
ProcessOutputReaderWorkerThreadArguments args;
try
{
args = (ProcessOutputReaderWorkerThreadArguments)data;
}
catch
{
return;
}
try
{
char[] readBuffer = new char[args.ReadBufferSize];
while (!args.Exit)
{
if (args.Process == null)
{
return;
}
if (args.Process.HasExited)
{
return;
}
if (args.Process.StandardOutput.EndOfStream)
{
return;
}
int readBytes = this.Process.StandardOutput.Read(readBuffer, 0, readBuffer.Length);
args.IntermediateDataStore.Append(readBuffer, 0, readBytes);
this.FireOnDataRead(new String(readBuffer, 0, readBytes));
}
}
catch (ThreadAbortException)
{
if (!args.Process.HasExited)
{
args.Process.Kill();
}
}
}
}
In addition you need the ProcessOutputReaderWorkerThreadArguments class which looks like this:
/// <summary>
/// Represents the ProcessOutputReaderWorkerThreadArguments class.
/// </summary>
public class ProcessOutputReaderWorkerThreadArguments
{
/// <summary>
/// Represents the read buffer size,
/// </summary>
private int readBufferSize;
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReaderWorkerThreadArguments"/> class.
/// </summary>
/// <param name="process">The process.</param>
/// <param name="intermediateDataStore">The intermediate data store.</param>
public ProcessOutputReaderWorkerThreadArguments(Process process, StringBuilder intermediateDataStore)
{
this.ReadBufferSize = 128;
this.Exit = false;
this.Process = process;
this.IntermediateDataStore = intermediateDataStore;
}
/// <summary>
/// Gets or sets a value indicating whether the thread shall exit or not.
/// </summary>
public bool Exit
{
get;
set;
}
/// <summary>
/// Gets or sets the read buffer size in bytes.
/// </summary>
/// <exception cref="System.ArgumentOutOfRangeException">Is thrown if the specified value is not greather than 0.</exception>
public int ReadBufferSize
{
get
{
return this.readBufferSize;
}
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException("value", "The specified value for \"ReadBufferSize\" must be greater than 0.");
}
this.readBufferSize = value;
}
}
/// <summary>
/// Gets the process.
/// </summary>
public Process Process
{
get;
private set;
}
/// <summary>
/// Gets the intermediate data store.
/// </summary>
public StringBuilder IntermediateDataStore
{
get;
private set;
}
}
And the ProcessOutputReaderEventArgs class which looks like this:
/// <summary>
/// Represents the ProcessOutputReaderEventArgs class.
/// </summary>
public class ProcessOutputReaderEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="ProcessOutputReaderEventArgs"/> class.
/// </summary>
/// <param name="intermediateDataStore">The reference to the intermediate data store.</param>
/// <param name="newData">The new data that has been read.</param>
public ProcessOutputReaderEventArgs(StringBuilder intermediateDataStore, string newData)
{
this.IntermediateDataStore = intermediateDataStore;
this.NewData = newData;
}
/// <summary>
/// Gets the reference to the intermediate data store.
/// </summary>
public StringBuilder IntermediateDataStore
{
get;
private set;
}
/// <summary>
/// Gets the new data that has been read.
/// </summary>
public string NewData
{
get;
private set;
}
}
Some example how to achieve this (attention, code not tested and can be improved)
ProcessOutputReader por = new ProcessOutputReader(proc1);
por.StartReading();
// Do whatever you want here
// (e.g. sleep or whatever)
por.StopReading();
// Now you have everything that has been read in por.Lines
The class would look like:
public class ProcessOutputReader
{
public ProcessOutputReader(Process process)
{
this.Process = process;
this.Lines = new List<string>();
}
public List<string> Lines
{
get;
private set;
}
public Process Process
{
get;
private set;
}
private Thread ReaderThread
{
get;
set;
}
public void StartReading()
{
if (this.ReaderThread == null)
{
this.ReaderThread = new Thread(new ThreadStart(ReaderWorker));
}
if (!this.ReaderThread.IsAlive)
{
this.ReaderThread.Start();
}
}
public void StopReading()
{
if (this.ReaderThread != null)
{
if (this.ReaderThread.IsAlive)
{
this.ReaderThread.Abort();
this.ReaderThread.Join();
}
}
}
private void ReaderWorker()
{
try
{
while (!this.Process.HasExited)
{
string data = this.Process.StandardOutput.ReadLine();
this.Lines.Add(data);
}
}
catch (ThreadAbortException)
{
if (!this.Process.HasExited)
{
this.Process.Kill();
}
}
}
}
I'm trying to build a display for a die roll. What I want to do is flicker images of random faces on the die, then end with the face that shows the number rolled. After this happens, I want the function to continue and return the number of the die roll. Here's what I have
public int RollDie()
{
RollNum = dieRoll.Next(1, 7);
DispCount = 0;
Timer Time = new Timer();
Time.Interval = TimerInterval;
Time.Tick += DisplayRollHandler;
Time.Start();
System.Threading.Thread DispThread = new System.Threading.Thread(Time.Start);
DispThread.Start();
DispThread.Join();
return RollNum;
}
private void DisplayRollHandler(object sender, EventArgs evt)
{
if (DispCount < TargetDispCount)
{
Random Nums = new Random();
Display.BackgroundImage = Faces[Nums.Next(0, 6)];
DispCount++;
}
else
{
((Timer)sender).Stop();
Display.BackgroundImage = Faces[RollNum - 1];
}
}
where dieRoll is a random object and Display is a Panel. The image flicker works, and it does return the number of the roll consistently. Unfortunately, it doesn't wait for the display flicker to finish before continuing, which is a problem when I have automatic messages that pop up after the die is rolled.
I'm a fairly inexperienced programmer, so I'm probably missing a really basic concept. I know that if I could abstract this into a method call, I can wait for a method call to finish, but I can't figure out how to do that without using a Thread.Sleep call and freezing the program.
Any suggestions?
There is a fundamental error with this solution in that the randomizer should never be instantiated within the dice object itself. A simple test will show why. Simply add another dice object to the form and roll both at the same time. Notice something funny? They are always the same!
This is because, as a default, the randomizer used the current time to seed the generator. Creating two (or more) objects in the same part of code will result in all of the dice objects having the same seed, and thus the same result when rolled every time.
A better solution would involve creating a static singleton class that would handle all the rolling (randomizing) all the dice for you.
Here is a quick example (using a Dice class that is a bit more generic):
public static class DiceRoller
{
private static Random _roller;
public static void RollDice(Dice dice)
{
if (dice.Faces.Count < 1)
throw new InvalidOperationException("A dice must contain at least 1 side to be rolled.");
if (_roller == null)
_roller = new Random();
int index = _roller.Next(dice.Faces.Count);
dice.SetFacingIndex(index);
}
}
Just wrote a little Dice class which will provide the desired values to you:
public class Dice
{
private Random _Random;
private BackgroundWorker _Worker;
/// <summary>
/// Initializes a new instance of the <see cref="Dice"/> class.
/// </summary>
public Dice()
{
_Random = new Random();
InitializeDefaultValues();
InitializeBackgroundWorker();
}
/// <summary>
/// Occurs when the dice finished rolling.
/// </summary>
public event EventHandler Rolled;
/// <summary>
/// Occurs while the dice is rolling and the value has changed.
/// </summary>
public event EventHandler RollingChanged;
/// <summary>
/// Gets or sets the including maximum value that the dice can return.
/// </summary>
/// <value>
/// The maximum value.
/// </value>
[DefaultValue(6)]
public int Maximum { get; set; }
/// <summary>
/// Gets or sets the including minimum value that the dice can return.
/// </summary>
/// <value>
/// The minimum.
/// </value>
[DefaultValue(1)]
public int Minimum { get; set; }
/// <summary>
/// Gets the result that this dice currently has.
/// </summary>
public int Result { get; private set; }
/// <summary>
/// Gets or sets the duration of the rolling.
/// </summary>
/// <value>
/// The duration of the rolling.
/// </value>
[DefaultValue(typeof(TimeSpan), "00:00:03")]
public TimeSpan RollingDuration { get; set; }
/// <summary>
/// Starts rolling the dice.
/// </summary>
public void Roll()
{
if (!_Worker.IsBusy)
{
CheckParameters();
_Worker.RunWorkerAsync();
}
}
private void CheckParameters()
{
if (Minimum >= Maximum)
{
throw new InvalidOperationException("Minimum value must be less than the Maximum value.");
}
if (RollingDuration <= TimeSpan.Zero)
{
throw new InvalidOperationException("The RollingDuration must be greater zero.");
}
}
private void InitializeBackgroundWorker()
{
_Worker = new BackgroundWorker();
_Worker.WorkerReportsProgress = true;
_Worker.DoWork += OnWorkerDoWork;
_Worker.ProgressChanged += OnWorkerProgressChanged;
_Worker.RunWorkerCompleted += OnWorkerRunWorkerCompleted;
}
private void InitializeDefaultValues()
{
Minimum = 1;
Maximum = 6;
Result = Minimum;
RollingDuration = TimeSpan.FromSeconds(3);
}
private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
{
var finishTime = DateTime.UtcNow + RollingDuration;
while (finishTime > DateTime.UtcNow)
{
Result = _Random.Next(Minimum, Maximum + 1);
_Worker.ReportProgress(0);
// ToDo: Improve sleep times for more realistic rolling.
Thread.Sleep(50);
}
}
private void OnWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
RaiseEvent(RollingChanged);
}
private void OnWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RaiseEvent(Rolled);
}
private void RaiseEvent(EventHandler handler)
{
var temp = handler;
if (temp != null)
{
temp(this, EventArgs.Empty);
}
}
}
In a first and simple example i simply added a button to a form (buttonRoll) and a label (labelDiceResult) and added the follwing code (don't forget to add the initialize method into the form constructor):
private void InitializeDice()
{
_Dice = new Dice();
_Dice.RollingChanged += OnDiceRollingChanged;
_Dice.Rolled += OnDiceRolled;
}
void OnDiceRolled(object sender, EventArgs e)
{
buttonRoll.Enabled = true;
}
void OnDiceRollingChanged(object sender, EventArgs e)
{
// ToDo: Select desired picture from image list depending on _Dice.Result
labelDiceResult.Text = _Dice.Result.ToString();
}
private void OnButtonRollClick(object sender, EventArgs e)
{
buttonRoll.Enabled = false;
_Dice.Roll();
}
As a last step i would maybe tweak the Thread.Sleep(50) call to use different values over time by using a calculated list depending on the rising part of a sinus and the desired duration to let the dice slow down over time. But i let this part open for the reader (or a next question).