I have a List<Passenger> that contains information about their trips with my bus.
Each time I perform a trip I generate a random number of passengers (anywhere from 0 to 10) and I record them when they exit the bus.
I run the program anywhere between 10 to 100.000 times and record the passengers to the list only when they exit the bus. On the final stop, everyone gets kicked out.
I then use this code to write every passenger's trip to a text file.
Code
This code get called directly inside static void Main of Program.cs right after the cycles have completed running.
private static async Task LogData(StatManager statManager)
{
await File.WriteAllLinesAsync("test_output_1.txt", statManager.LogAllData());
}
Passenger.cs
public int ID {get; private set;}
public int stops {get; private set;} = 0;
public TripInfo trip {get; private set;}
public Passenger(int passenger_id, int stop_entered)
{
ID = passenger_id;
trip = new TripInfo();
trip.first_stop = stop_entered;
}
public void ExitBus(int exit_stop)
{
//get called once the passenger exits the bus
//at this point, the passenger is added to the main list
trip.exit_stop = exit_stop;
}
public void ArrivedAtStop()
{
stops++; //stop counter
}
public int[] GetStopsVisited()
{
int[] stops = new int[trip.stop_count];
for(int i = 0; i < trip.stop_count; i++)
{
stops[i] = trip.first_stop + i;
}
return stops;
}
public class TripInfo
{
public int first_stop;
public int exit_stop;
public int stop_count
{
get
{
if (exit_stop == null) return -1;
return exit_stop - first_stop;
}
}
}
StatManager.cs
public List<Passenger> LifetimePassengers = new List<Passenger>();
public int LifetimePassengerCount {get; private set;} = 0; //gets incremented in every cycle
private float mean;
private int LifetimeStops
{
get
{
return busManager.CurrentStop + 1; //gets an int that was incremented in every cycle
}
}
public String[] LogAllData()
{
List<String> toLog = new List<string>();
toLog.Add("LifetimeStops:"+LifetimeStops.ToString());
toLog.Add("LifetimePassengers:"+LifetimePassengerCount.ToString());
toLog.Add("IndividualPassengerData");
foreach(Passenger p in LifetimePassengers)
{
toLog.Add("-");
toLog.Add($"PassengerID:{p.ID}");
toLog.Add($"NumberOfStops:{p.stops}");
toLog.Add($"FirstStop:{p.trip.first_stop}");
toLog.Add($"ExitStop:{p.trip.exit_stop}");
string stops = String.Join(",", p.GetStopsVisited());
toLog.Add($"StopsVisited:[{stops}]");
}
return toLog.ToArray();
}
Output is as expected for the first lines but then I get this:
...
PassengerID:677
NumberOfStops:2
FirstStop:198
ExitStop:200
StopsVisited:[198,199]
PassengerID:678
NumberOfStops:2
FirstStop:198
ExitStop:200
StopsVisited:[198,199]
PassengerID:679
NumberOfStops:1
Fi
All data follows the above pattern of an ID and some trip information. But the data is incomplete. Above, the last passenger recorded is number 679 but the program ran for 1.000 stops and had a minimum of 1.000 passengers. Also the data stops abruptly with this unexpected Fi and does not complete the record of passenger number 679.
Furthermore, when I tried re-running the program, it did not output anything. I tried running it for amount of cycles: 5, 10 and 100, there was no difference and there was no output.
What can I do to solve this problem?
I think you need to follow this pattern calling async method from the main:
public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
private static async Task MainAsync()
{
//
var statManager = new StatManager();
// you code here ...
await LogData(statManager);
}
private static async Task LogData(StatManager statManager)
{
await File.WriteAllLinesAsync("test_output_1.txt",
statManager.LogAllData());
}
Related
Trying to setup Reactive Extension for a simple example. Eg: there is random Number Generator, and anytime Number Divisible by three is spotted, "Write Hello" and Observe the Event. However it does not seem to be working. Feel free to edit line, or rewrite code to make work correctly. Its a quick two page copy into Console Program.
Eventually, want number generator to work in a stream , while SeeNumber observes in a Console program for one minute.
Code follows this resource: Reactive Extensions
Goals which can be incorporated in code:
Make the Number Generator streaming
Also reading, are Subjects are good in RX? Feel free to modify code without subjects if true,
Main.cs
static void Main(string[] args)
{
var generateNumber = new GenerateNumber();
var seeNumber = new SeeNumber(generateNumber);
generateNumber.DivideByThreeSpotted.Subscribe(seeNumber.OnDivideByThreeSpotted);
Console.WriteLine(generateNumber.NumberValue);
Console.ReadKey();
}
Produce Number:
public class GenerateNumber
{
public int NumberValue { get; set; }
public GenerateNumber()
{
NumberValue = CreateData();
}
public Subject<GenerateNumber> divideByThreeSpotted = new Subject<GenerateNumber>();
public IObservable<GenerateNumber> DivideByThreeSpotted
{
get { return this.divideByThreeSpotted; }
}
public int CreateData()
{
Random random = new Random();
return random.Next(0, 100);
}
public void SpotDivideByThree(GenerateNumber generateNumber)
{
try
{
if (NumberValue % 3 == 0)
{
Console.WriteLine("Number Can be Divided by 3");
this.divideByThreeSpotted.OnNext(generateNumber);
}
}
catch (Exception exception)
{
this.divideByThreeSpotted.OnError(exception);
}
}
}
See Number:
public class SeeNumber : IDisposable
{
private IDisposable divideByThreeSpottedSubscription;
public SeeNumber(GenerateNumber generateNumber)
{
this.divideByThreeSpottedSubscription = generateNumber.DivideByThreeSpotted.Subscribe(this.OnDivideByThreeSpotted);
}
public void Dispose()
{
this.divideByThreeSpottedSubscription.Dispose();
}
public void OnDivideByThreeSpotted(GenerateNumber generateNumber)
{
GenerateNumber spottedDivisibleByThree = generateNumber;
}
}
Creating a simple example from tutorial,
I'm trying to improve my program for Fibonacci numbers using of memoization:
public class MyGlobals
{
public long TotNum { get; set; }
public long[] MyNumbers { get; set; }
public void GetParam()
{
Console.Write("n = ");
this.TotNum = long.Parse(Console.ReadLine());
this.MyNumbers = new long[this.TotNum + 1];
// set all numbers to -1
for (int i = 0; i < this.MyNumbers.Length; i++)
{
this.MyNumbers[i] = -1;
}
}
}
class Program
{
static void Main(string[] args)
{
MyGlobals globVariable = new MyGlobals();
globVariable.GetParam();
long n = globVariable.TotNum;
Console.WriteLine("Fib ({0}) = {1}", n, Fibonacci(n));
Console.ReadKey();
}
static long Fibonacci(long n)
{
MyGlobals globVariable = new MyGlobals();
if (n <= 1)
{
return 1;
}
if (globVariable.MyNumbers[n] != -1)
{
return globVariable.MyNumbers[n];
}
else
{
globVariable.MyNumbers[n] = Fibonacci(n - 1) + Fibonacci(n - 2);
}
return globVariable.MyNumbers[n];
}
}
I'm trying to do something like feed an array by -1 in MyGlobals class for further using MyNumbers array in Fibonacci static method.
Until line where I'm starting to call recursive fibonacci method it holds MyNumbers array in memory. But in Fibonacci method, when I create new instance of MyGlobals class for calling MyNumbers array is this array empty... What I'm doing wrong. Can you anybody help me on this, please. Thank you very much in forward.
Declare globVariable as a static member of the Program class like so:
class Program
{
static MyGlobals globVariable = new MyGlobals();
static void Main(string[] args)
{
globVariable.GetParam();
long n = globVariable.TotNum;
Console.WriteLine("Fib ({0}) = {1}", n, Fibonacci(n));
Console.ReadKey();
}
static long Fibonacci(long n)
{
if (n <= 1)
{
return 1;
}
if (globVariable.MyNumbers[n] != -1)
{
return globVariable.MyNumbers[n];
}
else
{
globVariable.MyNumbers[n] = Fibonacci(n - 1) + Fibonacci(n - 2);
}
return globVariable.MyNumbers[n];
}
}
There is no such thing as global variables in C#. The problem you're having relates to instances of nonstatic classes.
You effectively have three separate units in your code:
One class that asks for input, holds this input and holds an array of result variables (MyGlobals). This is in fact way too much for a single class and should ultimately be split up.
One method that calculates Fibonacci numbers and stores them into the previous class (Fibonacci).
A Program class and Main() method which host your console application.
Now your problem is that you don't know how to access the array of inputs stored in 1 from method 2. There are various ways to solve that, each with their own cons and pros. The most obvious one is to pass a reference.
But before that, clean up your code: give classes and methods meaningful names, and extract logic into separate classes.
Here you'll remain with three classes:
public class FibonacciInput
{
public void GetParam()
{
// Your "MyGlobals" logic
}
}
Then the calculation logic:
public class FibonacciCalculator
{
public long Fibonacci(long index, long[] range)
{
// Your "Fibonacci()" logic
}
}
And the program:
class Program
{
static void Main(string[] args)
{
FibonacciInput input = new FibonacciInput();
FibonacciCalculator calculator = new FibonacciCalculator();
input.GetParam();
long n = input.TotNum;
Console.WriteLine("Fib ({0}) = {1}", n, calculator.Fibonacci(n, input.MyNumbers));
Console.ReadKey();
}
}
Now your calculator doesn't know anything about your input, and the need for "global variables" goes away.
The point is that the Fibonacci() method needs two things: the index (the Nth Fibonacci number it should calculate) and an array to work with (which you initialized on beforehand).
So by calling calculator.Fibonacci(n, input.MyNumbers), you solve all problems at once.
Well, may be it's not really answers your question but i'd refactor your code dividing it to logical parts where each part is only responsible for one thing :
UI
Global variables
Class that knows how to work with fibo sequence
Program (entry point)
Refactored code may look something among the lines of :
// Globals should be static
public static class MyGlobals
{
public static long TotNum { get; private set; }
public static long[] MyNumbers { get; private set; }
public static void SetNum(long num)
{
TotNum = num;
MyNumbers = new long[TotNum + 1];
}
}
// interacts with UI
public static class UIHelper
{
public static long GetParam()
{
Console.Write("n = ");
var number = long.Parse(Console.ReadLine());
return number;
}
}
// Knows how to calc fibo
static class Fibo
{
static long Calc(long[] nums, long n)
{
... calc fibonacci logic
}
}
class Program
{
static void Main(string[] args)
{
// now we can use them all
// first lets get value from console
var num = UIHelper.GetParam();
// set global variables with this value
MyGlobals.SetNum(num);
// output result :
Console.WriteLine("Fib ({0}) = {1}", n, Fibo.Calc(MyGlobals.MyNumbers, MyGlobals.TotalNum));
Console.ReadKey();
}
}
P.S.
Whether to send global values as parameters to Fibo.Calc() method or to access them directly from inside of it it's up to you. I vote for first option because it makes it easier to test this method by passing mock data.
Inside my application I have list of persons from my database.
For every person I must call 5 (for now) services to search for some informations.
If service returns info I' adding it to that person (list of orders for specific person)
Because services work independent I thought I could try to run them parallel.
I've created my code as so:
using System;
using System.Collections.Generic;
using System.Threading;
namespace Testy
{
internal class Program
{
internal class Person
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> Orders { get; private set; }
public Person()
{
// thanks for tip #juharr
Orders = new List<string>();
}
public void AddOrder(string order)
{
lock (Orders) //access across threads
{
Orders.Add(order);
}
}
}
internal class Service
{
public int Id { get; private set; }
public Service(int id)
{
Id = id;
}
//I get error when I use IList instead of List
public void Search(ref List<Person> list)
{
foreach (Person p in list)
{
lock (p) //should I lock Person here? and like this???
{
Search(p);
}
}
}
private void Search(Person p)
{
Thread.Sleep(50);
p.AddOrder(string.Format("test order from {0,2}",
Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(100);
}
}
private static void Main()
{
//here I load my services from external dll's
var services = new List<Service>();
for (int i = 1; i <= 5; i++)
{
services.Add(new Service(i));
}
//sample data load from db
var persons = new List<Person>();
for (int i = 1; i <= 10; i++)
{
persons.Add(
new Person {Id = i,
Name = string.Format("Test {0}", i)});
}
Console.WriteLine("Number of services: {0}", services.Count);
Console.WriteLine("Number of persons: {0}", persons.Count);
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = services.Count;
foreach (Service service in services)
{
new Thread(() =>
{
service.Search(ref persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
// Wait for workers.
resetEvent.WaitOne();
foreach (Person p in persons)
{
Console.WriteLine("{0,2} Person name: {1}",p.Id,p.Name);
if (null != p.Orders)
{
Console.WriteLine(" Orders:");
foreach (string order in p.Orders)
{
Console.WriteLine(" Order: {0}", order);
}
}
else
{
Console.WriteLine(" No orders!");
}
}
Console.ReadLine();
}
}
}
I have 2 problems with my code:
When I run my app I should get list of 10 persons and for every person 5 orders, but from time to time (ones for 3-5 runs) for first person I get only 4 orders. How I can prevent such behaviour? solved! thanks to #juharr
How can I report progress from my threads? What I would like to get is one Function from my Program class that will be called every time order is added from service - I need that to show some kind of progress for every report. I was trying solution described here: https://stackoverflow.com/a/3874184/965722, but I'm wondering if there is an easier way. Ideally I would like to add delegate to Service class and place all Thread code there.
How should I add event and delegate to Service class and how to subscribe to it in Main method?
I'm using .NET 3.5
I've added this code to be able to get progress reports:
using System;
using System.Collections.Generic;
using System.Threading;
namespace Testy
{
internal class Program
{
public class ServiceEventArgs : EventArgs
{
public ServiceEventArgs(int sId, int progress)
{
SId = sId;
Progress = progress;
}
public int SId { get; private set; }
public int Progress { get; private set; }
}
internal class Person
{
private static readonly object ordersLock = new object();
public int Id { get; set; }
public string Name { get; set; }
public List<string> Orders { get; private set; }
public Person()
{
Orders = new List<string>();
}
public void AddOrder(string order)
{
lock (ordersLock) //access across threads
{
Orders.Add(order);
}
}
}
internal class Service
{
public event EventHandler<ServiceEventArgs> ReportProgress;
public int Id { get; private set; }
public string Name { get; private set; }
private int counter;
public Service(int id, string name)
{
Id = id;
Name = name;
}
public void Search(List<Person> list) //I get error when I use IList instead of List
{
counter = 0;
foreach (Person p in list)
{
counter++;
Search(p);
Thread.Sleep(3000);
}
}
private void Search(Person p)
{
p.AddOrder(string.Format("Order from {0,2}", Thread.CurrentThread.ManagedThreadId));
EventHandler<ServiceEventArgs> handler = ReportProgress;
if (handler != null)
{
var e = new ServiceEventArgs(Id, counter);
handler(this, e);
}
}
}
private static void Main()
{
const int count = 5;
var services = new List<Service>();
for (int i = 1; i <= count; i++)
{
services.Add(new Service(i, "Service " + i));
}
var persons = new List<Person>();
for (int i = 1; i <= 10; i++)
{
persons.Add(new Person {Id = i, Name = string.Format("Test {0}", i)});
}
Console.WriteLine("Number of services: {0}", services.Count);
Console.WriteLine("Number of persons: {0}", persons.Count);
Console.WriteLine("Press ENTER to start...");
Console.ReadLine();
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = services.Count;
foreach (Service service in services)
{
new Thread(() =>
{
service.ReportProgress += service_ReportProgress;
service.Search(persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
// Wait for workers.
resetEvent.WaitOne();
foreach (Person p in persons)
{
if (p.Orders.Count != count)
Console.WriteLine("{0,2} Person name: {1}, orders: {2}", p.Id, p.Name, p.Orders.Count);
}
Console.WriteLine("END :)");
Console.ReadLine();
}
private static void service_ReportProgress(object sender, ServiceEventArgs e)
{
Console.CursorLeft = 0;
Console.CursorTop = e.SId;
Console.WriteLine("Id: {0,2}, Name: {1,2} - Progress: {2,2}", e.SId, ((Service) sender).Name, e.Progress);
}
}
}
I've added custom EventArgs, event for Service class.
In this configuration I should have 5 services running, but only 3 of them report progress.
I imagined that if I have 5 services I should have 5 events (5 lines showing progress).
This is probably because of threads, but I have no ideas how to solve this.
Sample output now looks like this:
Number of services: 5
Number of persons: 10
Press ENTER to start...
Id: 3, Name: Service 3 - Progress: 10
Id: 4, Name: Service 4 - Progress: 10
Id: 5, Name: Service 5 - Progress: 19
END :)
It should look like this:
Number of services: 5
Number of persons: 10
Press ENTER to start...
Id: 1, Name: Service 1 - Progress: 10
Id: 2, Name: Service 2 - Progress: 10
Id: 3, Name: Service 3 - Progress: 10
Id: 4, Name: Service 4 - Progress: 10
Id: 5, Name: Service 5 - Progress: 10
END :)
Last edit
I've moved all my thread creation to separate class ServiceManager now my code looks like so:
using System;
using System.Collections.Generic;
using System.Threading;
namespace Testy
{
internal class Program
{
public class ServiceEventArgs : EventArgs
{
public ServiceEventArgs(int sId, int progress)
{
SId = sId;
Progress = progress;
}
public int SId { get; private set; } // service id
public int Progress { get; private set; }
}
internal class Person
{
private static readonly object ordersLock = new object();
public int Id { get; set; }
public string Name { get; set; }
public List<string> Orders { get; private set; }
public Person()
{
Orders = new List<string>();
}
public void AddOrder(string order)
{
lock (ordersLock) //access across threads
{
Orders.Add(order);
}
}
}
internal class Service
{
public event EventHandler<ServiceEventArgs> ReportProgress;
public int Id { get; private set; }
public string Name { get; private set; }
public Service(int id, string name)
{
Id = id;
Name = name;
}
public void Search(List<Person> list)
{
int counter = 0;
foreach (Person p in list)
{
counter++;
Search(p);
var e = new ServiceEventArgs(Id, counter);
OnReportProgress(e);
}
}
private void Search(Person p)
{
p.AddOrder(string.Format("Order from {0,2}", Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(50*Id);
}
protected virtual void OnReportProgress(ServiceEventArgs e)
{
var handler = ReportProgress;
if (handler != null)
{
handler(this, e);
}
}
}
internal static class ServiceManager
{
private static IList<Service> _services;
public static IList<Service> Services
{
get
{
if (null == _services)
Reload();
return _services;
}
}
public static void RunAll(List<Person> persons)
{
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = _services.Count;
foreach (Service service in _services)
{
var local = service;
local.ReportProgress += ServiceReportProgress;
new Thread(() =>
{
local.Search(persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
// Wait for workers.
resetEvent.WaitOne();
}
private static readonly object consoleLock = new object();
private static void ServiceReportProgress(object sender, ServiceEventArgs e)
{
lock (consoleLock)
{
Console.CursorTop = 1 + (e.SId - 1)*2;
int progress = (100*e.Progress)/100;
RenderConsoleProgress(progress, '■', ConsoleColor.Cyan, String.Format("{0} - {1,3}%", ((Service) sender).Name, progress));
}
}
private static void ConsoleMessage(string message)
{
Console.CursorLeft = 0;
int maxCharacterWidth = Console.WindowWidth - 1;
if (message.Length > maxCharacterWidth)
{
message = message.Substring(0, maxCharacterWidth - 3) + "...";
}
message = message + new string(' ', maxCharacterWidth - message.Length);
Console.Write(message);
}
private static void RenderConsoleProgress(int percentage, char progressBarCharacter,
ConsoleColor color, string message)
{
ConsoleColor originalColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.CursorLeft = 0;
int width = Console.WindowWidth - 1;
var newWidth = (int) ((width*percentage)/100d);
string progBar = new string(progressBarCharacter, newWidth) + new string(' ', width - newWidth);
Console.Write(progBar);
if (!String.IsNullOrEmpty(message))
{
Console.CursorTop++;
ConsoleMessage(message);
Console.CursorTop--;
}
Console.ForegroundColor = originalColor;
}
private static void Reload()
{
if (null == _services)
_services = new List<Service>();
else
_services.Clear();
for (int i = 1; i <= 5; i++)
{
_services.Add(new Service(i, "Service " + i));
}
}
}
private static void Main()
{
var services = ServiceManager.Services;
int count = services.Count;
var persons = new List<Person>();
for (int i = 1; i <= 100; i++)
{
persons.Add(new Person {Id = i, Name = string.Format("Test {0}", i)});
}
Console.WriteLine("Services: {0}, Persons: {1}", services.Count, persons.Count);
Console.WriteLine("Press ENTER to start...");
Console.ReadLine();
Console.Clear();
Console.CursorVisible = false;
ServiceManager.RunAll(persons);
foreach (Person p in persons)
{
if (p.Orders.Count != count)
Console.WriteLine("{0,2} Person name: {1}, orders: {2}", p.Id, p.Name, p.Orders.Count);
}
Console.CursorTop = 12;
Console.CursorLeft = 0;
Console.WriteLine("END :)");
Console.CursorVisible = true;
Console.ReadLine();
}
}
}
Basically you have a race condition with the create of the Orders. Imagine the following execution of two threads.
Thread 1 checks if Orders is null and it is.
Thread 2 checks if Orders is null and it is.
Thread 1 sets Orders to a new list.
Thread 1 gets the lock.
Thread 1 adds to the Orders list.
Thread 2 sets Order to a new list. (you just lost what Thread 1 added)
You need to include the creation of the Orders inside the lock.
public void AddOrder(string order)
{
lock (Orders) //access across threads
{
if (null == Orders)
Orders = new List<string>();
Orders.Add(order);
}
}
Or you really should create the Order list in a Person constructor
public Person()
{
Orders = new List<Order>();
}
Also you should really create a separate object for locking.
private object ordersLock = new object();
public void AddOrder(string order)
{
lock (ordersLock) //access across threads
{
Orders.Add(order);
}
}
EDIT:
In your foreach where you create the threads you need to create a local copy of the service to use inside the lambda expression. This is because the foreach will update the service variable and the thread can end up capturing the wrong variable. So something like this.
foreach (Service service in services)
{
Service local = service;
local.ReportProgress += service_ReportProgress;
new Thread(() =>
{
local.Search(persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
Note the subscription doesn't need to be inside the thread.
Alternatively you could move the creation of the thread inside the Search method of your Service class.
Additionally you might want to create a OnReportProgress method in the Service class like so:
protected virtual void OnReportProgress(ServiceEventArgs e)
{
EventHandler<ServiceEventArgs> handler = ReportProgress;
if (handler != null)
{
handler(this, e);
}
}
Then call that inside your Search method. Personally I'd call it in the public Search method and make the counter a local variable as well to allow for reuse of the Service object on another list.
Finally you will need an additional lock in the event handler when writing to the console to make sure one thread doesn't change the cursor position before another one writes it's output.
private static object consoleLock = new object();
private static void service_ReportProgress(object sender, ServiceEventArgs e)
{
lock (consoleLock)
{
Console.CursorLeft = 0;
Console.CursorTop = e.SId;
Console.WriteLine("Id: {0}, Name: {1} - Progress: {2}", e.SId, ((Service)sender).Name, e.Progress);
}
}
Also you might want to use Console.Clear() in the following locaiton:
...
Console.WriteLine("Number of services: {0}", services.Count);
Console.WriteLine("Number of persons: {0}", persons.Count);
Console.WriteLine("Press ENTER to start...");
Console.Clear();
Console.ReadLine();
...
And you'll need to update the cursor position before your write out your end statement.
Console.CursorTop = 6;
Console.WriteLine("END :)");
This might not totally answer your question (but I think you might have a race condition), when you start dealing with threads you need to implement proper synchronization when updating objects from different threads. You should make sure only one thread is able to update an instance of the person class at any given time.
The p.AddOrder( should have mutex that ensures only one thread is updating the Person object.
I have a main task that is spawning threads to do some work. When the work is completed it will write to the console.
My problem is that some of the threads that are created later will finish faster than those created earlier. However I need the writing to the console to be done in the same exact sequence as the thread was created.
So if a thread had completed its task, while some earlier threads had not, it has to wait till those earlier threads complete too.
public class DoRead
{
public DoRead()
{
}
private void StartReading()
{
int i = 1;
while (i < 10000)
{
Runner r = new Runner(i, "Work" + i.ToString());
r.StartThread();
i += 1;
}
}
}
internal class Runner : System.IDisposable
{
int _count;
string _work = "";
public Runner(int Count, string Work)
{
_count = Count;
_work = Work;
}
public void StartThread()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this);
}
public static void runThreadInPool(object obj)
{
((Runner)obj).run();
}
public void run()
{
try
{
Random r = new Random();
int num = r.Next(1000, 2000);
DateTime end = DateTime.Now.AddMilliseconds(num);
while (end > DateTime.Now)
{
}
Console.WriteLine(_count.ToString() + " : Done!");
}
catch
{
}
finally
{
_work = null;
}
}
public void Dispose()
{
this._work = null;
}
}
There may be a simpler way to do this than I used, (I'm used to .Net 4.0).
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ConsoleApplication5
{
class Program
{
public static readonly int numOfTasks = 100;
public static int numTasksLeft = numOfTasks;
public static readonly object TaskDecrementLock = new object();
static void Main(string[] args)
{
DoRead dr = new DoRead();
dr.StartReading();
int tmpNumTasks = numTasksLeft;
while ( tmpNumTasks > 0 )
{
Thread.Sleep(1000);
tmpNumTasks = numTasksLeft;
}
List<string> strings = new List<string>();
lock( DoRead.locker )
{
for (int i = 1; i <= Program.numOfTasks; i++)
{
strings.Add( DoRead.dicto[i] );
}
}
foreach (string s in strings)
{
Console.WriteLine(s);
}
Console.ReadLine();
}
public class DoRead
{
public static readonly object locker = new object();
public static Dictionary<int, string> dicto = new Dictionary<int, string>();
public DoRead()
{
}
public void StartReading()
{
int i = 1;
while (i <= Program.numOfTasks )
{
Runner r = new Runner(i, "Work" + i.ToString());
r.StartThread();
i += 1;
}
}
}
internal class Runner : System.IDisposable
{
int _count;
string _work = "";
public Runner(int Count, string Work)
{
_count = Count;
_work = Work;
}
public void StartThread()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(runThreadInPool), this);
}
public static void runThreadInPool(object obj)
{
Runner theRunner = ((Runner)obj);
string theString = theRunner.run();
lock (DoRead.locker)
{
DoRead.dicto.Add( theRunner._count, theString);
}
lock (Program.TaskDecrementLock)
{
Program.numTasksLeft--;
}
}
public string run()
{
try
{
Random r = new Random();
int num = r.Next(1000, 2000);
Thread.Sleep(num);
string theString = _count.ToString() + " : Done!";
return theString;
}
catch
{
}
finally
{
_work = null;
}
return "";
}
public void Dispose()
{
this._work = null;
}
}
}
}
Basically, I store the string you want printed from each task into a dictionary where the index is the task#. (I use a lock to make accessing the dictionary safe).
Next, so that the main program waits until all the background threads are done, I used another locked access to a NumTasksLeft variable.
I added stuff into the callback for the Runner.
It is bad practice to use busy loops, so I changed it to a Thread.Sleep( num ) statement.
Just change numOfTasks to 10000 to match your example.
I pull the return strings out of the dictionary in order, and then print it to the screen.
I'm sure you could refactor this to move or otherwise deal with the global variables, but this works.
Also, you might have noticed I didn't use the lock in the command
tmpNumTasks = numTasksLeft;
That's threadsafe, since numTasksLeft is an int which is read atomically on 32-bit computers and higher.
I don't know much on C#, but the whole idea of multi-threading is that you have multiple thread executing independently and you can never know which one will finish earlier (and you shouldn't expect earlier thread to end earlier).
One workaround is, instead writing out the finish message in the processing thread, have the processing thread setup a flag somewhere (probably a list with no of elements = no of thread spawned), and have a separate thread print out the finish message base on the flags in that list, and report up to the position that previous flag is consecutively "finished".
Honestly I don't feel that reasonable for you to print finish message like this anyway. I think changing the design is way better to have such meaningless "feature".
Typically, such requirements are met with an incrementing sequence number, much as you have already done.
Usually, the output from the processing threads is fed through a filter object that contains a list, (or dictionary), of all out-of-order result objects, 'holding them back' until all results with a lower seqeuence-number have come in. Again, similar to what you have already done.
What is not necessary is any kind of sleep() loop. The work threads themselves can operate the filter object, (which would beed a lock), or the work threads can producer-consumer-queue the results to an 'output thread' that operates the out-of-order filter.
This scheme works fine with pooled work threads, ie. those without continual create/terminate/destroy overhead.
I'm looking for a good method of tracking (counting) which workers have failed when queued with a Threadpool and using WaitHandle.WaitAll() for all threads to finish.
Is Interlocking a counter a good technique or is there a more robust strategy?
Okay, here's an approach that you could take. I've encapsulated the data that we want to track into a class TrackedWorkers. There is a constructor on this class that enables you to set how many workers will be working. Then, the workers are launched using LaunchWorkers which requires a delegate that eats an object and returns a bool. The object represents the input to the worker and the bool represents success or failure depending on true or false being the return value, respectively.
So basically what we do we have an array to track worker state. We launch the workers and set the status corresponding to that worker depending on the return value from the worker. When the worker returns, we set an AutoResetEvent and WaitHandle.WaitAll for all the AutoResetEvents to be set.
Note that there is an nested class to track the work (the delegate) the worker is supposed to do, the input to that work, and an ID used to set the status AutoResetEvent corresponding to that thread.
Note very carefully that once the work is done we are not holding a reference to the work delegate func nor to the input. This is important so that we don't accidentally prevent stuff from being garbage collected.
There are methods for getting the status of a particular worker, as well as all the indexes of the workers that succeeded and all the indexes of the workers that failed.
One last note: I do not consider this code production ready. It is merely a sketch of the approach that I would take. You need to take care to add testing, exception handling and other such details.
class TrackedWorkers {
class WorkerState {
public object Input { get; private set; }
public int ID { get; private set; }
public Func<object, bool> Func { get; private set; }
public WorkerState(Func<object, bool> func, object input, int id) {
Func = func;
Input = input;
ID = id;
}
}
AutoResetEvent[] events;
bool[] statuses;
bool _workComplete;
int _number;
public TrackedWorkers(int number) {
if (number <= 0 || number > 64) {
throw new ArgumentOutOfRangeException(
"number",
"number must be positive and at most 64"
);
}
this._number = number;
events = new AutoResetEvent[number];
statuses = new bool[number];
_workComplete = false;
}
void Initialize() {
_workComplete = false;
for (int i = 0; i < _number; i++) {
events[i] = new AutoResetEvent(false);
statuses[i] = true;
}
}
void DoWork(object state) {
WorkerState ws = (WorkerState)state;
statuses[ws.ID] = ws.Func(ws.Input);
events[ws.ID].Set();
}
public void LaunchWorkers(Func<object, bool> func, object[] inputs) {
Initialize();
for (int i = 0; i < _number; i++) {
WorkerState ws = new WorkerState(func, inputs[i], i);
ThreadPool.QueueUserWorkItem(this.DoWork, ws);
}
WaitHandle.WaitAll(events);
_workComplete = true;
}
void ThrowIfWorkIsNotDone() {
if (!_workComplete) {
throw new InvalidOperationException("work not complete");
}
}
public bool GetWorkerStatus(int i) {
ThrowIfWorkIsNotDone();
return statuses[i];
}
public IEnumerable<int> SuccessfulWorkers {
get {
return WorkersWhere(b => b);
}
}
public IEnumerable<int> FailedWorkers {
get {
return WorkersWhere(b => !b);
}
}
IEnumerable<int> WorkersWhere(Predicate<bool> predicate) {
ThrowIfWorkIsNotDone();
for (int i = 0; i < _number; i++) {
if (predicate(statuses[i])) {
yield return i;
}
}
}
}
Sample usage:
class Program {
static Random rg = new Random();
static object lockObject = new object();
static void Main(string[] args) {
int count = 64;
Pair[] pairs = new Pair[count];
for(int i = 0; i < count; i++) {
pairs[i] = new Pair(i, 2 * i);
}
TrackedWorkers workers = new TrackedWorkers(count);
workers.LaunchWorkers(SleepAndAdd, pairs.Cast<object>().ToArray());
Console.WriteLine(
"Number successful: {0}",
workers.SuccessfulWorkers.Count()
);
Console.WriteLine(
"Number failed: {0}",
workers.FailedWorkers.Count()
);
}
static bool SleepAndAdd(object o) {
Pair pair = (Pair)o;
int timeout;
double d;
lock (lockObject) {
timeout = rg.Next(1000);
d = rg.NextDouble();
}
Thread.Sleep(timeout);
bool success = d < 0.5;
if (success) {
Console.WriteLine(pair.First + pair.Second);
}
return (success);
}
}
The above program is going to launch sixty-four threads. The ith thread has the task of adding the numbers i and 2 * i and printing the result to the console. However, I have added a random amount of sleep (less than one second) to simulate busyness and I flip a coin to determine success or failure of the thread. Those that succeed print the sum they were tasked with and return true. Those that fail print nothing and return false.
Here I have used
struct Pair {
public int First { get; private set; }
public int Second { get; private set; }
public Pair(int first, int second) : this() {
this.First = first;
this.Second = second;
}
}