I'm currently running into a problem with multithreading and accessing a static list. A static list holds all items with several properties. The items are identified with a Guid. A main work thread changes some properties for any item in the static list. The child threads all have their own Guid, with this Guid they read their own item in the static list. And after a specific event they remove their assigned element from the static list.
To get to the source I have broken down my code to the essential methods and classes. The work thread has the following simplified code
public void RunWork()
{
Random random = new Random();
Int32 index = -1;
while (!Kill)
{
Thread.Sleep(1);
if (MainWindow.Clients != null)
{
index = random.Next(0, MainWindow.Clients.Count);
MainWindow.Clients[index].State = MainWindow.RandomString(9);
}
}
}
Each child thread has the following simplified code
public void RunChild()
{
Random random = new Random();
while (!Kill)
{
Thread.Sleep(100);
if (MainWindow.Clients.Any(x => x.Id == Id))
{
this.State = MainWindow.Clients.First(x => x.Id == Id).State;
}
Thread.Sleep(random.Next(50));
if (random.Next(100) % 90 == 0)
{
Kill = true;
MainWindow.Clients.RemoveAll(x => x.Id == Id);
}
}
}
If a child removes itself from the MainWindow.Clients list the work thread throws a exception, that the index it is trying to access does not exist.
I have added lock statments around every access of MainWindow.Clients but this does not prevent the work thread from accessing a deleted item. I have also tried Monitor.Enter(MainWindow.Clients) and Monitor.Exit(MainWindow.Clients) but with the same result as with lock.
The static list MainWindow.Clients is created before any thread runs and never gets recreated or disposed.
If the lock statement is set around this block of code in the RunWork() method
lock (MainWindow.Clients)
{
Thread.Sleep(1);
if (MainWindow.Clients != null)
{
index = random.Next(0, MainWindow.Clients.Count);
MainWindow.Clients[index].State = MainWindow.RandomString(9);
}
}
Why does it not block the child threads from changing the list between the lines
where the random index is set and the list gets accessed?
Update 1:
The following code still throws a IndexOutOfRangeException at MainWindow.Clients[index].State = MainWindow.RandomString(9);:
public void RunWork()
{
Random random = new Random();
Int32 index = -1;
while (!Kill)
{
Thread.Sleep(1);
if (MainWindow.Clients != null)
{
lock (MainWindow.Clients)
{
index = random.Next(0, MainWindow.Clients.Count);
MainWindow.Clients[index].State = MainWindow.RandomString(9);
}
}
}
}
public void RunChild()
{
Random random = new Random();
while (!Kill)
{
Thread.Sleep(100);
if (MainWindow.Clients.Any(x => x.Id == Id))
{
this.State = MainWindow.Clients.First(x => x.Id == Id).State;
}
Thread.Sleep(random.Next(50));
if (random.Next(100) % 90 == 0)
{
Kill = true;
lock (MainWindow.Clients)
{
MainWindow.Clients.RemoveAll(x => x.Id == Id);
}
}
}
}
Update 2: Here is the complete code for the quick sample application
Update 3: I have edited my code and wrapped all accesses of MainWindow.Clients with lock statements. But still the threads access the variable while it is locked:
I'm not sure what exactly you are trying to achieve, but I've written something that might help you find the correct solution. Sorry for the lack of correctness - tight schedule ;-)
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace ConcurrentCollectionTest
{
internal class Client
{
public string State
{
get; set;
}
public string Name
{
get;
internal set;
}
}
internal class MainWindow
{
private ConcurrentDictionary<int, Client> _dict = new ConcurrentDictionary<int, Client>();
public IDictionary<int, Client> Clients
{
get
{
return _dict;
}
}
}
internal class Program
{
private static bool killAll = false;
private static MainWindow mainWindow = new MainWindow();
private static int id = -100;
private static string state = "Initial";
private static Random random = new Random();
private static object lockObject = new object();
internal static string RandomString(int v)
{
int k = random.Next(0, v);
return k.ToString();
}
public static void RunChild()
{
Debug.WriteLine($"child running {Thread.CurrentThread.Name}");
bool killThis = false;
while (!killThis && !killAll)
{
Thread.Sleep(100);
Client client = null;
if (mainWindow.Clients.TryGetValue(id, out client))
{
state = client.State;
}
Thread.Sleep(random.Next(50));
if (random.Next(100) % 90 == 0)
{
Debug.WriteLine($"killing {Thread.CurrentThread.Name}");
killThis = true;
lock (lockObject)
{
mainWindow.Clients.Remove(id);
}
}
}
}
public static void RunWork()
{
Console.WriteLine("RunWork");
Random random = new Random();
Int32 index = -1;
while (!killAll)
{
if (!mainWindow.Clients.Any())
{
killAll = true;
break;
}
Thread.Sleep(100);
// edit: still need lock here as count can change in between
Client client = null;
lock (lockObject)
{
index = random.Next(0, mainWindow.Clients.Count);
client = mainWindow.Clients[index];
}
Debug.WriteLine($"Changing {client.Name}");
client.State = RandomString(9);
}
Console.WriteLine("Worker killed");
}
private static void Main(string[] args)
{
Console.WriteLine("Starting. Enter id or kill");
for (int i = 0; i < 100; i++)
{
mainWindow.Clients.Add(i, new Client
{
Name = $"Client {i:000}",
State = "Unknown"
});
}
var worker = new Thread(RunWork);
worker.Start();
var threadList = new List<Thread>();
threadList.Add(worker);
for (int i = 0; i < 10; i++)
{
var thread = new Thread(RunChild)
{
Name = $"Child {i:00}"
};
threadList.Add(thread);
thread.Start();
}
while (!killAll)
{
var str = Console.ReadLine();
if (str.Equals("kill", StringComparison.InvariantCultureIgnoreCase))
{
killAll = true;
break;
}
int enteredId = -1;
if (int.TryParse(str, out enteredId))
{
id = enteredId;
}
}
foreach (var thread in threadList)
{
thread.Join();
}
Console.WriteLine("all dead");
}
}
}
Related
I developed an MPI test program where the master node distributes work to the worker nodes.
The worker node uses comm.Send() to request work and the master node checks with comm.ImmediateProbe if any of the worker nodes wants to request some work. If a request is available it is read with comm.Receive and the work is sent to the worker for processing.
When I run my test program with mpiexec.exe on a single host, either localhost or also a remote host everything works as expected, but when I run it on two hosts at the same time
the Send on the remote host blocks and the master nodes ImmediateProbe never receives the message sent from the worker on the remote host.
I run the program with mpiexec.exe -wdir \\DESKTOP-58QONBS\MPITest -hosts 2 DESKTOP-58QONBS 2 LAPTOP-L8F7AN5R 1 MPITest.exe
I'm new to MPI, so maybe I am doing something wrong I just could not figure out yet why the behaviour is like this when using two hosts at the same time.
The full code is below:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
namespace MPITest
{
public abstract class MPIMasterWorkerBase<TWork, TResult>
where TWork : class
where TResult : class
{
protected abstract void Initialize(bool isMaster);
protected abstract void Main();
protected abstract void ProcessResult(TResult result);
protected abstract TResult ProcessWork(TWork work);
protected abstract TWork GetWork();
private volatile bool terminate = false;
private Thread thread;
private MPI.Intracommunicator comm;
public void Run(string[] args)
{
MPI.Environment.Run(ref args, comm =>
{
this.comm = comm;
if (comm.Size < 2)
{
Console.WriteLine("At least 2 processes are required.");
return;
}
if (comm.Rank == 0)
{
Initialize(isMaster: true);
thread = new Thread(MasterThread);
thread.Start();
Main();
terminate = true;
thread.Join();
}
else
{
Initialize(isMaster: false);
thread = new Thread(WorkerThread);
thread.Start();
thread.Join();
}
});
}
private void MasterThread()
{
Console.WriteLine($"MasterStart {MPI.Environment.ProcessorName}");
var done = new bool[comm.Size];
done[0] = true;
while (!done.All(x => x == true))
{
for (int i = 1; i < comm.Size; i++)
{
if (comm.ImmediateProbe(i, 0) != null)
{
Console.WriteLine($"Receive: {i}");
comm.Receive<int>(i, 0);
var work = GetWork();
if (work != null)
{
comm.Send(1, i, 0);
comm.Send(work, i, 0);
}
else
{
if (terminate)
{
comm.Send(-1, i, 0);
done[i] = true;
}
else
{
comm.Send(0, i, 0);
}
}
}
if (comm.ImmediateProbe(i, 1) != null)
{
var result = comm.Receive<TResult>(i, 1);
ProcessResult(result);
}
}
Thread.Sleep(1000);
}
Console.WriteLine("MasterStop");
}
private void WorkerThread()
{
Console.WriteLine($"WorkerStart: {comm.Rank} {MPI.Environment.ProcessorName}");
while (!terminate)
{
Thread.Sleep(1000);
Console.WriteLine($"Send: {comm.Rank}");
comm.Send(0, 0, 0);
var flag = comm.Receive<int>(0, 0);
if (flag == -1)
break;
else if (flag == 0)
continue;
var work = comm.Receive<TWork>(0, 0);
var result = ProcessWork(work);
comm.Send(result, 0, 1);
}
Console.WriteLine($"WorkerStop: {comm.Rank}");
}
}
[Serializable]
public class WorkItem
{
public int Id { get; set; }
}
public class MPITest : MPIMasterWorkerBase<WorkItem, WorkItem>
{
private ConcurrentQueue<WorkItem> queue = new();
private int id;
protected override void Initialize(bool isMaster)
{
}
protected override void Main()
{
var startTime = DateTime.UtcNow;
while ((DateTime.UtcNow - startTime).TotalSeconds < 10)
{
for (int i = 0; i < 2; i++)
queue.Enqueue(new WorkItem { Id = id++ });
Thread.Sleep(1000);
}
}
protected override WorkItem GetWork()
{
if (queue.TryDequeue(out var result))
return result;
return null;
}
protected override WorkItem ProcessWork(WorkItem work)
{
Console.WriteLine($"Processing Work {work.Id}");
return work;
}
protected override void ProcessResult(WorkItem result)
{
Console.WriteLine($"Process Result {result.Id}");
}
}
class Program
{
static void Main(string[] args)
{
new MPITest().Run(args);
}
}
}
The comm.Send was blocking, but after some minutes of waiting the program started to work.
The issues were caused by the VirtualBox Host-Only Network Adapter that was also installed on the system. Disabling this adapter in the network settings resolved all the issues.
I have a static class A which has a public static ConcurrentQueue.
This class has a method which calls a method of another static class B.
In this method of static class B, a worker thread is started which polls for items in the static ConcurrentQueue of class A. If found, it dequeues the item and processes it.
The same method in class B also starts another thread which checks if the queue of class A is being constantly filled. If the filling occasionally is somehow stuck, a call to a static method in class A is made which resets and restarts the process.
And here comes the problem: When I restart the filling, items are inserted again into the static queue of static class A. But my thread in static class B which checks for items in the queue does not find any items anymore.
So my idea is that in class B I somehow lost the reference to the original static queue of class A. I think I oversee here some principles of static classes, but can not figure out what exactly.
EDIT: Here are some more details.
Main:
public static void Main(string[] args)
{
B.Initialize(8);
A.StartDataStream();
Console.ReadKey();
}
Class A:
class A
{
private static ACOMObject MyCOMObjekt;
public static ConcurrentQueue<PriceItem> Prices = new ConcurrentQueue<PriceItem>();
public static void StartDataStream()
{
DataStream myDataStream = GetDataStream();
FillStream(myDataStream);
B.StartHeartbeatCheck();
Console.WriteLine("Press Key to Exit Stream Call.");
Console.ReadKey();
GC.KeepAlive(myDataStream);
GC.KeepAlive(MyCOMObjekt);
}
private static DataStream GetDataStream()
{
if (MyCOMObjekt== null)
{
MyCOMObjekt= new ACOMObject ();
}
return (DataStream) MyCOMObjekt.DataStream;
}
private static void FillStream(DataStream myDataStream)
{
foreach (var symbol in Symbols.SymbolList)
{
myDataStream.Add(symbolNr, 1);
}
myDataStream.Bid += new _IDataStreamEvents_BidEventHandler(myDataStream_Bid);
myDataStream.Ask += new _IDataStreamEvents_AskEventHandler(myDataStream_Ask);
}
private static void myDataStream_Bid(int SymbolNr, float Price, DateTime Time)
{
PriceItem p;
p.SymbolNr = SymbolNr;
p.Price = Price;
p.Time = Time;
p.IsBid = true;
Prices.Enqueue(p);
}
private static void myDataStream_Ask(int SymbolNr, float Price, DateTime Time)
{
PriceItem p;
p.SymbolNr = SymbolNr;
p.Price = Kurs;
p.Time = Zeit;
p.IsBid = false;
Prices.Enqueue(p);
}
public static void RestartMyCOMProcess()
{
try
{
Process proc = Process.GetProcessesByName("MyCOMProcess")[0];
proc.Kill();
}
catch (Exception)
{
//No Process = fine
}
MyCOMObjekt = null;
DataStream myDataStream = GetDataStream();
myDataStream.Bid -= new _IDataStreamEvents_BidEventHandler(myDataStream_Bid); //probably not necessary...
myDataStream.Ask -= new _IDataStreamEvents_AskEventHandler(myDataStream_Ask); //probably not necessary...
FillStream(myDataStream);
}
}
Class B:
class B
{
private static int countPrices; //Incremented every time a price is taken out of Price Queue
public static void Initialize(int numberOfWorkerThreads)
{
StartWorkers(numberOfWorkerThreads);
}
public static void StartWorkers(int number)
{
for (int j = 0; j < number; j++)
{
Thread t = new Thread(ScanForPrices);
t.Name = "ScanForPrices" + j;
t.Start();
}
}
private static void ScanForPrices()
{
try
{
while (true)
{
PriceItem p;
if (A.Prices.TryDequeue(out p))
{
if (p.IsBid)
{
AnalyzeBidPrice(p);
}
else
{
HandleAskPrice(p);
}
Interlocked.Increment(ref countPrices)
}
else
{
Thread.Sleep(1);
}
}
}
catch (ThreadAbortException)
{
Console.WriteLine("Price Scan Thread aborted.");
}
}
public static void StartHeartbeatCheck()
{
Thread t = new Thread(CheckHeartBeat);
t.Name = "CheckHeartBeat";
t.Start();
}
private static void CheckHeartBeat()
{
TimeSpan start = new TimeSpan(09, 0, 0); //09 o'clock
TimeSpan end = new TimeSpan(20, 0, 0); //20 o'clock
TimeSpan stopTime = new TimeSpan(20, 5, 0); //20 o'clock and 5 minutes
int countSuccessiveBlockings = 0;
try
{
while (true)
{
Thread.Sleep(5000);
TimeSpan now = DateTime.Now.TimeOfDay;
if ((now > start) && (now < end))
{
int countOld = countPrices;
Thread.Sleep(1000);
if (countOld == countPrices)
{
Console.WriteLine(DateTime.Now + ": Price Stream blocked!");
countSuccessiveBlockings++;
}
else
{
countSuccessiveBlockings = 0;
}
if (countSuccessiveBlockings > 2)
{
A.RestartMyCOMProcess();
countSuccessiveBlockings = 0;
}
}
}
}
catch (ThreadAbortException)
{
Console.WriteLine("Heartbeat Thread aborted.");
}
}
}
I am testing simple caching logic in c#. Here is my CacheManager class:
public class CacheManager
{
static List<string> _list = new List<string>();
static readonly object _syncobject = new object();
public static void ReloadList()
{
List<string> list = new List<string>();
Random r = new Random();
var count = r.Next(10);
for (int i = 0; i < count; i++)
{
list.Add("list" + i);
}
//lock (_syncobject)
{
_list = list;
}
}
public static IEnumerable<string> GetList()
{
//lock (_syncobject)
{
return _list;
}
}
}
Below is the class which spawns many threads consuming CacheManager:
class Program
{
static void Main(string[] args)
{
//threads for re-loading the list
for (int i = 0; i < 3; i++)
{
Thread reloadThread = new Thread(ReloadList);
reloadThread.Start();
}
//threads for getting the list
for (int i = 0; i < 10; i++)
{
Thread tget = new Thread(PrintList);
tget.Start();
}
//threads for getting the list and storing in local variable then use it
for (int i = 0; i < 10; i++)
{
Thread tget = new Thread(PrintListWithLocalVariable);
tget.Start();
}
Console.ReadKey();
}
private static void ReloadList()
{
do
{
Console.WriteLine("Reloading **********");
CacheManager.ReloadList();
} while (true);
}
private static void PrintList()
{
do
{
foreach (var item in CacheManager.GetList())
{
if (item == null)
throw new Exception("i == null");
Console.WriteLine(item);
}
} while (true);
}
private static void PrintListWithLocalVariable()
{
do
{
var list = CacheManager.GetList();
foreach (var listitem in list)
{
var i = list.FirstOrDefault(x => x.Equals(listitem));
if (i == null)
throw new Exception("i == null");
Console.WriteLine("Printing with Local variable:" + listitem);
}
} while (true);
}
}
My understanding was we should lock the _list variable in CacheManager, but doesn't look like we need that. I ran the above test for an hour or so but didn't get any error. While ReloadThread is reloading the random number of list items, other threads which are looping through the list, i thought might have issues. Can anybody explain me why the program is running without issues?
Thanks.
_list is a static variable, meaning that every instance of CacheManager will share the same instance of _list. Access to _list should indeed be locked to prevent concurrency issues. As someone mentioned in a comment, a ConcurrentCollection should be used instead of List(of T) as well, as List(of T) is not thread-safe.
The earlier post seems not very clear, so after some testing, I reopened this post with much more simplified words, hope somebody could help.
My singleton observable was turned from multiple source of I/O events, means they're concurrently raised up in underlying, based on testing (to prove Rx is not thread safe) and RX design guideline, I made it serialized, see that lock(...):
public class EventFireCenter
{
public static event EventHandler<GTCommandTerminalEventArg> OnTerminalEventArrived;
private static object syncObject = new object();
public static void TestFireDummyEventWithId(int id)
{
lock (syncObject)
{
var safe = OnTerminalEventArrived;
if (safe != null)
{
safe(null, new GTCommandTerminalEventArg(id));
}
}
}
}
This is the singleton Observable:
public class UnsolicitedEventCenter
{
private readonly static IObservable<int> publisher;
static UnsolicitedEventCenter()
{
publisher = Observable.FromEventPattern<GTCommandTerminalEventArg>(typeof(EventFireCenter), "OnTerminalEventArrived")
.Select(s => s.EventArgs.Id);
}
private UnsolicitedEventCenter() { }
/// <summary>
/// Gets the Publisher property to start observe an observable sequence.
/// </summary>
public static IObservable<int> Publisher { get { return publisher; } }
}
The scenario of Subscribe(...) can be described by following code, you can see the Subscribe(...) could be called concurrently in different threads:
for (var i = 0; i < concurrentCount; i++)
{
var safe = i;
Scheduler.Default.Schedule(() =>
{
IDisposable dsp = null;
dsp = UnsolicitedEventCenter.Publisher
.Timeout(TimeSpan.FromMilliseconds(8000))
.Where(incomingValue => incomingValue == safe)
.ObserveOn(Scheduler.Default)
//.Take(1)
.Subscribe((incomingEvent) =>
{
Interlocked.Increment(ref onNextCalledTimes);
dsp.Dispose();
}
, ex =>
{
Interlocked.Increment(ref timeoutExceptionOccurredTimes);
lock (timedOutEventIds)
{
// mark this id has been timed out, only for unit testing result check.
timedOutEventIds.Add(safe);
}
dsp.Dispose();
});
Interlocked.Increment(ref threadPoolQueuedTaskCount);
});
}
As pointed out times by experienced people, call Dispose() in OnNext(...) is not recommended, but let's ignore it here since the code was from production.
Now the problem is randomly that .Timeout(TimeSpan.FromMilliseconds(8000)) is not working, the ex was never called, anyone could see any abnormal in the code?
for testing, I setup the stress testing, but so far, I didn't reproduced it, while in production, it appeared several times per day. Just in case, I pasted all the testing code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Rx
{
class Program
{
static void Main(string[] args)
{
// avoid thread creation delay in thread pool.
ThreadPool.SetMinThreads(200, 50);
// let the test run for 100 times
for (int t = 0; t < 100; t++)
{
Console.WriteLine("");
Console.WriteLine("======Current running times: " + t);
// at meantime, 150 XXX.Subscribe(...) will be called.
const int concurrentCount = 150;
// how many fake event will be fire to santisfy that 150 XXX.Subscribe(...).
const int fireFakeEventCount = 40;
int timeoutExceptionOccurredTimes = 0;
var timedOutEventIds = new List<int>();
int onNextCalledTimes = 0;
int threadPoolQueuedTaskCount = 0;
for (var i = 0; i < concurrentCount; i++)
{
var safe = i;
Scheduler.Default.Schedule(() =>
{
IDisposable dsp = null;
dsp = UnsolicitedEventCenter.Publisher
.Timeout(TimeSpan.FromMilliseconds(8000))
.Where(incomingValue => incomingValue == safe)
.ObserveOn(Scheduler.Default)
//.Take(1)
.Subscribe((incomingEvent) =>
{
Interlocked.Increment(ref onNextCalledTimes);
dsp.Dispose();
}
, ex =>
{
Interlocked.Increment(ref timeoutExceptionOccurredTimes);
lock (timedOutEventIds)
{
// mark this id has been timed out, only for unit testing result check.
timedOutEventIds.Add(safe);
}
dsp.Dispose();
});
Interlocked.Increment(ref threadPoolQueuedTaskCount);
});
}
Console.WriteLine("Starting fire event: " + DateTime.Now.ToString("HH:mm:ss.ffff"));
int threadPoolQueuedTaskCount1 = 0;
// simulate a concurrent event fire
for (int i = 0; i < fireFakeEventCount; i++)
{
var safe = i;
Scheduler.Default.Schedule(() =>
{
EventFireCenter.TestFireDummyEventWithId(safe);
Interlocked.Increment(ref threadPoolQueuedTaskCount1);
});
}
// make sure all proceeding task has been done in threadPool.
while (threadPoolQueuedTaskCount < concurrentCount)
{
Thread.Sleep(1000);
}
// make sure all proceeding task has been done in threadPool.
while (threadPoolQueuedTaskCount1 < fireFakeEventCount)
{
Thread.Sleep(100);
}
Console.WriteLine("Finished fire event: " + DateTime.Now.ToString("HH:mm:ss.ffff"));
// sleep a time which >3000ms.
Thread.Sleep(8000);
Console.WriteLine("timeoutExceptionOccurredTimes: " + timeoutExceptionOccurredTimes);
Console.WriteLine("onNextCalledTimes: " + onNextCalledTimes);
if ((concurrentCount - fireFakeEventCount) != timeoutExceptionOccurredTimes)
{
try
{
Console.WriteLine("Non timeout fired for these ids: " +
Enumerable.Range(0, concurrentCount)
.Except(timedOutEventIds).Except(Enumerable.Range(0, fireFakeEventCount)).Select(i => i.ToString())
.Aggregate((acc, n) => acc + "," + n));
}
catch (Exception ex) { Console.WriteLine("faild to output timedout ids..."); }
break;
}
if (fireFakeEventCount != onNextCalledTimes)
{
Console.WriteLine("onNextOccurredTimes assert failed");
break;
}
if ((concurrentCount - fireFakeEventCount) != timeoutExceptionOccurredTimes)
{
Console.WriteLine("timeoutExceptionOccurredTimes assert failed");
break;
}
}
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("DONE!");
Console.ReadLine();
}
}
public class EventFireCenter
{
public static event EventHandler<GTCommandTerminalEventArg> OnTerminalEventArrived;
private static object syncObject = new object();
public static void TestFireDummyEventWithId(int id)
{
lock (syncObject)
{
var safe = OnTerminalEventArrived;
if (safe != null)
{
safe(null, new GTCommandTerminalEventArg(id));
}
}
}
}
public class UnsolicitedEventCenter
{
private readonly static IObservable<int> publisher;
static UnsolicitedEventCenter()
{
publisher = Observable.FromEventPattern<GTCommandTerminalEventArg>(typeof(EventFireCenter), "OnTerminalEventArrived")
.Select(s => s.EventArgs.Id);
}
private UnsolicitedEventCenter() { }
/// <summary>
/// Gets the Publisher property to start observe an observable sequence.
/// </summary>
public static IObservable<int> Publisher { get { return publisher; } }
}
public class GTCommandTerminalEventArg : System.EventArgs
{
public GTCommandTerminalEventArg(int id)
{
this.Id = id;
}
public int Id { get; private set; }
}
}
Most likely the Timeout is not triggering because you have it before the Where filter. This means that all events are flowing through and resetting the timer, and then most of the events get filtered by the Where clause. To your subscribing observer, it will seem like it never gets a result and the timeout never triggers. Move the Timeout to be after the Where and you should now have a system that times out individual observers if they do not get their expected event on time.
I'm trying to create a Sheduler (for fun) but it fails. Strange is that when i'm debugging in step-over style, my programm works fine, but when i'm removing all breakpoints it freezes after printing last value. So question: why does it freezes? Second: i'm using Thread.Resume and Thread.Suspend, but they are marked as obsolete. How can i avoid it?
Code is below:
using System;
using System.Collections.Generic;
using System.Threading;
namespace ConsoleApplication143
{
internal class Program
{
private static void Main()
{
var rrs = new RobinRoundSheduler(2, () =>
{
for (int i = 0; i < 2; i++)
{
Console.WriteLine("{0} {1}", i,
Thread.CurrentThread.ManagedThreadId);
}
}) {TimeForTask = new TimeSpan(1)};
rrs.Start();
Console.ReadKey();
}
}
internal class RobinRoundSheduler
{
private readonly LinkedList<Thread> _threads;
public TimeSpan TimeForTask { get; set; }
public RobinRoundSheduler(int taskCount, ThreadStart start)
{
TimeForTask = TimeSpan.FromSeconds(1);
_threads = new LinkedList<Thread>();
for (int i = 0; i < taskCount; i++)
{
_threads.AddLast(new Thread(start));
}
}
public void Start()
{
while (_threads.Count > 0)
{
var list = new List<Thread>();
foreach (var thread in _threads)
{
lock (thread)
{
if (thread.ThreadState == ThreadState.Unstarted)
thread.Start();
else
thread.Resume();
}
thread.Join(TimeForTask);
lock (thread)
{
if (thread.ThreadState == ThreadState.Stopped || thread.ThreadState == ThreadState.Aborted)
list.Add(thread);
else
{
thread.Suspend();
}
}
}
list.ForEach(thread => _threads.Remove(thread));
}
}
}
}
it seems having a deadlock problem due to Thread.Suspend() method but i dunno another alternative to suspend a thread withoud adding checks for ManualResetEvents in calling methods. But i want to call method knows nothing about multithreading.