I Have issue with parllel Tasks
My Code
namespace ITDevices
{
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
/*Device Modal*/
public class Device
{
public string IP { get; set; }
public string Name { get; set; }
public string MAC { get; set; }
}
/*Entry Class*/
class Program
{
static async void Main(string[] args)
{
List<Task<Device>> Tasks = new List<Task<Device>>();
for(int i=2;i==0;i--)
{
Tasks.Add(Task.Factory.StartNew<Device>(
()=> {
Device free = Helper.GetFreeDevice();
return free;
}
));
}
await Task.WhenAll(Tasks.ToArray());
foreach(Task<Device> item in Tasks)
{
Console.WriteLine(item.Result.IP);
}
Console.ReadLine();
}
}
/*Devices Helper*/
static class Helper
{
public static List<Device> UsedDevices = new List<Device>();
public static Device GetFreeDevice()
{
List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).SingleOrDefault();
if (FreeDevice != null)
UsedDevices.Add(FreeDevice);
return FreeDevice;
}
}
}
//Output
//192.168.1.15
//192.168.1.15
But expected output must be
//192.168.1.15
//192.168.1.20
When debugging the project
all tasks execute GetFreeDevice() function at same time line by line
I need to make tasks wait while current GetFreeDevice() function execution done .. or any thing helpful
THANKS ALL
Several problems must be solved to make it work properly:
You probably reversed condition in for loop, because int i = 2; i == 0; i-- will do nothing. Replace i == 0 with i != 0
async Main method doesn't make sense (see for example this blog) and actually doesn't even compile in Visual Studio. To fix this, you can for example wait for tasks to complete synchronously (use .Wait() instead of await)
To prevent multiple threads to run GetFreeDevice() method simultaneously, simply place lock around code that uses shared objects - which is whole method body in your case.
Because you create new list of OnlineDevices each time GetFreeDevice() method is called, UsedDevices.Contains(x) will not work as expected. By default, objects are compared by their reference. So .Contains(x) will compare Device objects in UsedDevices list (which were put there in one of previous calls) with newly created Device objects, which will never be equal (references of these object will be different despite IP, Name and MAC being the same). To fix this, you could either override Equals() and GetHashCode() methods on Device class, or (as i did) create just one static list of Device objects.
You must replace SingleOrDefault() with FirstOrDefault(). With SingleOrDefault(), program would throw exception if there is more than one unused device, while FirstOrDefault() will take first unused device even if there is more than one.
Full source code with all proposed fixes:
namespace ITDevices
{
/*Device Modal*/
public class Device
{
public string IP { get; set; }
public string Name { get; set; }
public string MAC { get; set; }
}
/*Entry Class*/
class Program
{
static void Main(string[] args)
{
List<Task<Device>> Tasks = new List<Task<Device>>();
for (int i = 2; i != 0; i--)
{
Tasks.Add(Task.Factory.StartNew<Device>(
() => {
Device free = Helper.GetFreeDevice();
return free;
}
));
}
Task.WhenAll(Tasks.ToArray()).Wait();
foreach (Task<Device> item in Tasks)
{
Console.WriteLine(item.Result.IP);
}
Console.ReadLine();
}
}
/*Devices Helper*/
static class Helper
{
public static List<Device> UsedDevices = new List<Device>();
static List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
static Object LockObject = new Object();
public static Device GetFreeDevice()
{
lock (LockObject)
{
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).FirstOrDefault();
if (FreeDevice != null)
UsedDevices.Add(FreeDevice);
return FreeDevice;
}
}
}
}
try:
public static Device GetFreeDevice()
{
List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).SingleOrDefault();
if (FreeDevice != null)
lock (UsedDevices)
UsedDevices.Add(FreeDevice);
return FreeDevice;
}
------------------------ UPDATE
try:
public static Device GetFreeDevice()
{
List<Device> OnlineDevices = new List<Device>()
{
new Device { IP="192.168.1.15",Name="PerryLabtop",MAC="AC:DS:F2:CC:2D:7A"},
new Device { IP="192.168.1.20",Name="MAYA-PC",MAC="7D:E9:2C:FF:E7:2D"},
new Device { IP="192.168.1.2",Name="server",MAC="D8:C2:A4:DC:E5:3A"}
};
lock (UsedDevices)
{
Device FreeDevice = OnlineDevices.Where(x => !UsedDevices.Contains(x)).SingleOrDefault();
if (FreeDevice != null)
UsedDevices.Add(FreeDevice);
}
return FreeDevice;
}
Related
I'm going to get another user and enter which month he wants and find the quarter.
Thought of writing the code inside a class as I need more training on how to use classes.
The program asks whose month it is and I can enter. But now when I type "January" only programs crash.
I assume that it should show which quarter "january" is in
namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Write a month");
var mc = new MyClass();
mc.month = Convert.ToString(Console.ReadLine());
}
public class MyClass
{
public string month;
public string Prop
{
get
{
return month;
}
set
{ if (Prop == "january")
{
Console.WriteLine("1.quarter");
}
}
}
}
}
}
Consider presenting a menu which in the case below uses a NuGet package Spectre.Console and docs. This gives you an opportunity to work with classes and ensures, in this case input is a valid month along with reties and how to exit.
First a class for the menu.
public class MonthItem
{
public int Index { get; }
public string Name { get; }
public MonthItem(int index, string name)
{
Index = index;
Name = name;
}
public override string ToString() => Name;
}
Class which creates the menu
class MenuOperations
{
public static SelectionPrompt<MonthItem> SelectionPrompt()
{
var menuItemList = Enumerable.Range(1, 12).Select((index) =>
new MonthItem(index, DateTimeFormatInfo.CurrentInfo.GetMonthName(index)))
.ToList();
menuItemList.Add(new MonthItem(-1, "Exit"));
SelectionPrompt<MonthItem> menu = new()
{
HighlightStyle = new Style(Color.Black, Color.White, Decoration.None)
};
menu.Title("[yellow]Select a month[/]");
menu.PageSize = 14;
menu.AddChoices(menuItemList);
return menu;
}
}
Present menu, get selection and show details or exit.
static void Main(string[] args)
{
while (true)
{
Console.Clear();
var menuItem = AnsiConsole.Prompt(MenuOperations.SelectionPrompt());
if (menuItem.Index != -1)
{
AnsiConsole.MarkupLine($"[b]{menuItem.Name}[/] index is [b]{menuItem.Index}[/]");
Console.ReadLine();
}
else
{
return;
}
}
}
I tested your code and your program is not crashing. The program is actually completing and therefore the console is closing due to execution completing. So you can see what I mean try changing your code to this. You will see your code loop and the console will not close. It will also display what the user types in for mc.month
static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Write a month");
var mc = new MyClass();
mc.month = Convert.ToString(Console.ReadLine());
Console.WriteLine(mc.month);
}
}
On a side note, not really how I would use a class. You might want to also rethink that. Don't normally see writelines in class.
Well, I would like to do my own benchmarking system like spark in Minecraft (https://github.com/lucko/spark):
I'm using Harmony lib (https://github.com/pardeike/Harmony) which allows me to interact/modify methods and allows me to add a Prefix/Postfix on each call that will help me out with this stack.
The basic structure has something similar to (https://github.com/pardeike/Harmony/issues/355):
[HarmonyPatch]
class MyPatches
{
static IEnumerable<MethodBase> TargetMethods()
{
return AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly())
.SelectMany(type => type.GetMethods())
.Where(method => method.ReturnType != typeof(void) && method.Name.StartsWith("Do"));
}
static void Prefix(out Stopwatch __state, MethodBase __originalMethod)
{
__state = Stopwatch.StartNew();
// ...
}
static void Postfix(Stopwatch __state, MethodBase __originalMethod)
{
__state.Stop();
// ....
}
}
The problem here is that the __originalMethod doesn't take care if it was called from A or B.
So for example, we had patched string.Join method. And the we call from A or B, where A or B, is the full callstack of this method.
So first, we need to assign a ID to this call, and we need to create a Tree-based structure (which is hard to serialize later), from here (https://stackoverflow.com/a/36649069/3286975):
public class TreeModel : Tree<TreeModel>
{
public int ID { get; set; }
public TreeModel() { }
public TreeModel(TreeModel parent) : base(parent) { }
}
public class Tree<T> where T : Tree<T>
{
protected Tree() : this(null) { }
protected Tree(T parent)
{
Parent=parent;
Children=new List<T>();
if(parent!=null)
{
parent.Children.Add(this as T);
}
}
public T Parent { get; set; }
public List<T> Children { get; set; }
public bool IsRoot { get { return Parent==null; } }
public T Root { get { return IsRoot?this as T:Parent.Root; } }
public T RecursiveFind(Predicate<T> check)
{
if(check(this as T)) return this as T;
foreach(var item in Children)
{
var result=item.RecursiveFind(check);
if(result!=null)
{
return result;
}
}
return null;
}
}
Now, the thing is that we need to fill the Tree as long as we iterate all the method and instructions got from Harmony. Forget about Harmony for a second, I will explain only two facts about it.
The lib allows you first to get all patched methods through IEnumerable<MethodBase> TargetMethods() so, you have the Assembly X passed through reflection and filtered all methods that are allowed to be patched (some of them broke Unity, so I decided to skip methods from UnityEngine., UnityEditor. and System.* namespaces).
And we have also the ReadMethodBody method (https://harmony.pardeike.net/api/HarmonyLib.PatchProcessor.html#HarmonyLib_PatchProcessor_ReadMethodBody_System_Reflection_MethodBase_) from a given MethodBase it returns all IL stack instructions.
So we can start to iterate over and over in order to get all instructions and fill the entire tree. This is what I wrote last night:
internal static class BenchmarkEnumerator
{
internal static Dictionary<MethodBase, int> Mappings { get; } = new Dictionary<MethodBase, int>();
internal static Dictionary<int, TreeModel> TreeIDs { get; } = new Dictionary<int, TreeModel>();
internal static Dictionary<MethodBase, BenchmarkTreeModel> TreeMappings { get; } = new Dictionary<MethodBase, BenchmarkTreeModel>();
private static HashSet<int> IDUsed { get; } = new HashSet<int>();
public static int GetID(this MethodBase method)
{
return GetID(method, out _);
}
public static int GetID(this MethodBase method, out bool contains)
{
// A > X = X1
// B > X = X2
if (!Mappings.ContainsKey(method))
{
var id = Mappings.Count;
Mappings.Add(method, Mappings.Count);
IDUsed.Add(id);
contains = false;
return id;
}
contains = true;
return Mappings[method];
}
public static int GetFreeID()
{
int id;
Random rnd = new Random();
do
{
id = rnd.Next();
} while (IDUsed.Contains(id));
IDUsed.Add(id);
return id;
}
public static BenchmarkCall GetCall(int id)
{
return TreeIDs[id]?.Call;
}
public static BenchmarkCall GetCall(this MethodBase method)
{
return TreeIDs[Mappings[method]]?.Call;
}
}
The BenchmarkEnumerator class allow us to differentiate between A or B, but it doesn't care about the full hierarchy, only from the parent MethodBase itself, so I need to write something complex to take in care of the full call stack, which I said I have a problem to understand.
Then we have the TargetMethods:
private static IEnumerable<MethodBase> TargetMethods()
{
Model = new BenchmarkTreeModel();
var sw = Stopwatch.StartNew();
//int i = 0;
return Filter.GetTargetMethods(method =>
{
try
{
var instructions = PatchProcessor.ReadMethodBody(method);
var i = method.GetID(out var contains);
var tree = new TreeModel
{
ID = i
};
if (contains)
{
//var lastId = i;
i = GetFreeID();
tree.ID = i;
tree.FillMethodName($"{method.GetMethodSignature()}_{i}"); // TODO: Check this
tree.Parent = null;
tree.Children = TreeMappings[method].Forest.First().Children; // ??
//DictionaryHelper.AddOrAppend(TreeMappings, method, tree);
TreeMappings[method].Forest.Add(tree);
TreeIDs.Add(i, tree);
Model.Forest.Add(tree);
// UNIT TESTING: All contained methods at this point will have a parent.
// string.Join is being added as a method by a instruction, so when we try to patch it, it will have already a reference on the dictionary
// Here, we check if the method was already added by a instruction CALL
// Logic: If the method is already contained by the mapping dictionary
// then, we will exit adding a new that will have the same childs but a new ID
return false;
}
TreeIDs.Add(i, tree);
tree.FillMethodName($"{method.GetMethodSignature()}_{i}"); // TODO: Check this
foreach (var pair in instructions)
{
var opcode = pair.Key;
if (opcode != OpCodes.Call || opcode != OpCodes.Callvirt) continue;
var childMethod = (MethodBase)pair.Value;
var id = childMethod.GetID(out var _contains);
var subTree = new TreeModel(tree)
{
ID = id
};
if (_contains)
{
id = GetFreeID();
subTree.ID = id;
subTree.FillMethodName($"{childMethod.GetMethodSignature()}_{id}"); // TODO: Check this
subTree.Parent = TreeIDs[i];
subTree.Children = TreeMappings[childMethod].Forest.First().Children;
TreeIDs.Add(id, subTree);
continue;
}
TreeIDs.Add(id, subTree);
subTree.FillMethodName($"{childMethod.GetMethodSignature()}_{id}");
tree.Children.Add(subTree);
TreeMappings.Add(childMethod, new BenchmarkTreeModel());
TreeMappings[childMethod].Forest.Add(subTree);
}
TreeMappings.Add(method, new BenchmarkTreeModel());
TreeMappings[method].Forest.Add(tree);
Model.Forest.Add(tree);
return true;
//var treeModel = new TreeModel();
}
catch (Exception ex)
{
//Debug.LogException(new Exception(method.GetMethodSignature(), ex));
return false;
}
}, sw);
//return methods;
}
The GetMethodSignature is something like:
public static string GetMethodSignature(this MethodBase method)
{
if (method == null) return null;
return method.DeclaringType == null ? method.Name : $"{method.DeclaringType.FullName}.{method.Name}";
}
I think I'll replace it with the MethodBase.ToString instead (what do you think?)
Also, we have the BenchmarkCall class which allow us to take in care how many times the call was done and how many time it has spent at all:
[Serializable]
public class BenchmarkCall
{
public string Method { get; set; }
public double SpentMilliseconds { get; set; }
public long SpentTicks { get; set; }
public double MinSpentMs { get; set; } = double.MaxValue;
public double MaxSpentMs { get; set; } = double.MinValue;
public long MinSpentTicks { get; set; } = long.MaxValue;
public long MaxSpentTicks { get; set; } = long.MinValue;
public double AvgMs => SpentMilliseconds / TimesCalled;
public double AvgTicks => SpentTicks / (double)TimesCalled;
public BenchmarkCall()
{
}
public BenchmarkCall(MethodBase method)
{
Method = method.GetMethodSignature();
}
public override string ToString()
{
if (TimesCalled > 0)
return "BenchmarkCall{\n" +
$"Ticks[SpentTicks={SpentTicks},MinTicks={MinSpentTicks},MaxTicks={MaxSpentTicks},AvgTicks={AvgTicks:F2}]\n" +
$"Ms[SpentMs={SpentMilliseconds:F2},MinMs={MinSpentMs:F2},MaxMs={MaxSpentMs:F2},AvgMs={AvgMs:F2}]\n" +
"}";
return "BenchmarkCall{}";
}
}
}
So I think that my next movement will be to differentiate between X method being called from A or B (Xa or Xb) taking care of the full hierarchy (which I'm not sure how to do) instead of the parent method that calls it, maybe the code I wrote has some to do it with it, but I'm not sure (last night I was so tired, so I didn't code it taking care those facts), build up a list of method signatures with different IDs, and then fill up the tree, ID 1 is Xa and ID 2 is Xb (where I have problems also filling up the tree).
Also I'll need to use the Transpiler in order to alter all code instructions, so if a method has:
void method() {
X1();
X2();
}
We will need to add 2 methods (like prefix/postfix) to measure each instruction call:
void method() {
Start(1);
X1();
End(1);
Start(2);
X2();
End(2);
}
This will be a hard task, but I hope somebody could guide me with this out.
Simple implementation; single process writing (although multiple tasks may write asynchronously) single process reading.
Most of the time it seems to be working fine, but every once in a while we get a message where the Body size is reasonable, but if you look at it in the Computer Management tool, it's nothing but '0's. This causes the XmlMessageFormatter on the reader to fail.
We added code that'll let us handle poisoned messages better, but we need the messages, so that alone is not acceptable.
Object:
public class SubscriptionData
{
public Guid SubscriptionInstanceId { get; set; }
public SubscriptionEntityTypes SubscriptionEntityType { get; set; }
public List<int> Positions { get; set; }
public List<EventInformation> Events { get; set; }
public int SubscriptionId { get; set; }
public SubscriptionData() { }
public SubscriptionData(SubscriptionEntityTypes entityType, List<int> positions, List<EventInformation> events, int subscriptionId)
{
SubscriptionEntityType = entityType;
Positions = positions;
Events = events;
SubscriptionId = subscriptionId;
SubscriptionInstanceId = Guid.NewGuid();
}
public override string ToString()
{
return $"Entity Type: {SubscriptionEntityType}, Instance Id: {SubscriptionInstanceId}, Events: {string.Join("/", Events)}, SubsId: {SubscriptionId}";
}
}
Writer:
private static void ConstructMessageQueue()
{
_messageQueue = MessageQueue.Exists(Queue) ?
new MessageQueue(Queue) : MessageQueue.Create(Queue);
_messageQueue.Label = QueueName;
}
private static void EnqueueSubscriptionData(SubscriptionEntityTypes entityType, List<int> positions, List<EventInformation> events, int subscriptionId)
{
Task.Run(() =>
{
var subsData = new SubscriptionData(entityType, positions, events, subscriptionId);
_logger.Info(ErrorLevel.Normal, $"Enqueuing subscription: {subsData}");
_messageQueue.Send(subsData);
});
}
Reader:
private void HandleNotifications()
{
var mq = new MessageQueue(Queue);
mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(SubscriptionData) });
while (!_cancellationToken.IsCancellationRequested)
{
Message message = null;
try
{
message = mq.Peek(TimeSpan.FromSeconds(5));
if (message != null)
{
var subsData = message.Body as SubscriptionData;
if (subsData == null)
continue;
_logger.Info(ErrorLevel.Normal, $"Processing subscription: {subsData}");
// Process the notification here
mq.Receive();
}
}
catch (MessageQueueException t) when (t.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
{
_logger.Info(ErrorLevel.Normal, $"Message Queue Peek Timeout");
continue;
}
catch (MessageQueueException t)
{
_logger.Exception(t, "MessageQueueException while processing message queue for notifications");
throw;
}
catch (Exception t)
{
_logger.Exception(t, "Exception while processing message queue for notifications");
}
}
}
If you're curious, I'm told that we peek and only receive after success so that we don't lose the message, but in reading up on this to try and help my coworker it looks like there are transactions.
The bad messages we get look like this.
It seems that Send method is not thread safe, you should not share MessageQueue object (your _messageQueue static variable) between multiple threads. It's discussed here:
Is MSMQ thread safe?
I doing a small project to map a network (routers only) using SNMP. In order to speed things up, I´m trying to have a pool of threads responsible for doing the jobs I need, apart from the first job which is done by the main thread.
At this time I have two jobs, one takes a parameter the other doesn´t:
UpdateDeviceInfo(NetworkDevice nd)
UpdateLinks() *not defined yet
What I´m trying to achieve is to have those working threads waiting for a job to
appear on a Queue<Action> and wait while it is empty. The main thread will add the first job and then wait for all workers, which might add more jobs, to finish before starting adding the second job and wake up the sleeping threads.
My problem/questions are:
How to define the Queue<Actions> so that I can insert the methods and the parameters if any. If not possible I could make all functions accept the same parameter.
How to launch the working threads indefinitely. I not sure where should I create the for(;;).
This is my code so far:
public enum DatabaseState
{
Empty = 0,
Learning = 1,
Updating = 2,
Stable = 3,
Exiting = 4
};
public class NetworkDB
{
public Dictionary<string, NetworkDevice> database;
private Queue<Action<NetworkDevice>> jobs;
private string _community;
private string _ipaddress;
private Object _statelock = new Object();
private DatabaseState _state = DatabaseState.Empty;
private readonly int workers = 4;
private Object _threadswaitinglock = new Object();
private int _threadswaiting = 0;
public Dictionary<string, NetworkDevice> Database { get => database; set => database = value; }
public NetworkDB(string community, string ipaddress)
{
_community = community;
_ipaddress = ipaddress;
database = new Dictionary<string, NetworkDevice>();
jobs = new Queue<Action<NetworkDevice>>();
}
public void Start()
{
NetworkDevice nd = SNMP.GetDeviceInfo(new IpAddress(_ipaddress), _community);
if (nd.Status > NetworkDeviceStatus.Unknown)
{
database.Add(nd.Id, nd);
_state = DatabaseState.Learning;
nd.Update(this); // The first job is done by the main thread
for (int i = 0; i < workers; i++)
{
Thread t = new Thread(JobRemove);
t.Start();
}
lock (_statelock)
{
if (_state == DatabaseState.Learning)
{
Monitor.Wait(_statelock);
}
}
lock (_statelock)
{
if (_state == DatabaseState.Updating)
{
Monitor.Wait(_statelock);
}
}
foreach (KeyValuePair<string, NetworkDevice> n in database)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(n.Value.Name + ".txt")
{
file.WriteLine(n);
}
}
}
}
public void JobInsert(Action<NetworkDevice> func, NetworkDevice nd)
{
lock (jobs)
{
jobs.Enqueue(item);
if (jobs.Count == 1)
{
// wake up any blocked dequeue
Monitor.Pulse(jobs);
}
}
}
public void JobRemove()
{
Action<NetworkDevice> item;
lock (jobs)
{
while (jobs.Count == 0)
{
lock (_threadswaitinglock)
{
_threadswaiting += 1;
if (_threadswaiting == workers)
Monitor.Pulse(_statelock);
}
Monitor.Wait(jobs);
}
lock (_threadswaitinglock)
{
_threadswaiting -= 1;
}
item = jobs.Dequeue();
item.Invoke();
}
}
public bool NetworkDeviceExists(NetworkDevice nd)
{
try
{
Monitor.Enter(database);
if (database.ContainsKey(nd.Id))
{
return true;
}
else
{
database.Add(nd.Id, nd);
Action<NetworkDevice> action = new Action<NetworkDevice>(UpdateDeviceInfo);
jobs.Enqueue(action);
return false;
}
}
finally
{
Monitor.Exit(database);
}
}
//Job1 - Learning -> Update device info
public void UpdateDeviceInfo(NetworkDevice nd)
{
nd.Update(this);
try
{
Monitor.Enter(database);
nd.Status = NetworkDeviceStatus.Self;
}
finally
{
Monitor.Exit(database);
}
}
//Job2 - Updating -> After Learning, create links between neighbours
private void UpdateLinks()
{
}
}
Your best bet seems like using a BlockingCollection instead of the Queue class. They behave effectively the same in terms of FIFO, but a BlockingCollection will let each of your threads block until an item can be taken by calling GetConsumingEnumerable or Take. Here is a complete example.
http://mikehadlow.blogspot.com/2012/11/using-blockingcollection-to-communicate.html?m=1
As for including the parameters, it seems like you could use closure to enclose the NetworkDevice itself and then just enqueue Action instead of Action<>
I have WCF method that take the scores of all players(clients) and add them to a list of them for sorting .
but Unfortunately when the first score sent to the server , the function add it to the list and sent the sorted list to the client , dose not wait for all other scores from other Players .
i tried to use async & await in order to delay continuation of the method for about 30 seconds as the following :
public List<Player> GetListOfWinners(int SentScore , string _SentPlayerID )
{
List<Player> PlayersList = new List<Player>();
//Add to the List
PlayersList.Add(new Player { PlayerID = _SentPlayerID, Score = SentScore });
DelayMethod();
//Order It
List<Player> SortedList = PlayersList.OrderByDescending(p => p.Score).ToList();
// sent fULL SORTHEDlist to the Clients
return SortedList;
}
private async void DelayMethod()
{
await Task.Delay(30000);
}
but it dosn`t work , so what should i do ?
I've created a very basic service as a sample to show how you can accomplish some of your goal. It is provided as a means to introduce you to constructs such as lock and ManualResetEvent.
In my ISudokuConcurrentService.cs:
namespace SudokuTest
{
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
[ServiceContract]
public interface ISudokuConcurrentService
{
[OperationContract]
SudokuGame ClientConnect();
[OperationContract]
List<Player> GetListOfWinners(int SentScore, string _SentPlayerID);
}
[DataContract]
public class SudokuGame
{
[DataMember]
public string PlayerID { get; set; }
[DataMember]
public int TimeLimitInSeconds { get; set; }
}
[DataContract]
public class Player
{
[DataMember]
public string PlayerID { get; set; }
[DataMember]
public int Score { get; set; }
}
}
In my SudokuConcurrentService.svc.cs:
namespace SudokuTest
{
using System.Collections.Generic;
using System.Linq;
using System.Threading;
public class SudokuConcurrentService : ISudokuConcurrentService
{
static int playerCount = 0;
static List<Player> PlayersList = null;
static object ListLock = new object();
const int TimeLimit = 120;
static ManualResetEvent AllPlayerResponseWait = new ManualResetEvent(false);
public SudokuGame ClientConnect()
{
lock (ListLock)
{
if (PlayersList != null)
{
// Already received a completed game response -- no new players.
return null;
}
}
return new SudokuGame()
{
PlayerID = "Player " + Interlocked.Increment(ref playerCount).ToString(),
TimeLimitInSeconds = TimeLimit
};
}
public List<Player> GetListOfWinners(int SentScore, string _SentPlayerID)
{
lock (ListLock)
{
// Initialize the list.
if (PlayersList == null) PlayersList = new List<Player>();
//Add to the List
PlayersList.Add(new Player { PlayerID = _SentPlayerID, Score = SentScore });
}
// Now decrement through the list of players to await all responses.
if (Interlocked.Decrement(ref playerCount) == 0)
{
// All responses received, unlock the wait.
AllPlayerResponseWait.Set();
}
// Wait on all responses, as necessary, up to 150% the timeout (no new players allowed one responses received,
// so the 150% should allow time for the last player to receive game and send response, but if any players have
// disconnected, we don't want to wait indefinitely).
AllPlayerResponseWait.WaitOne(TimeLimit * 1500);
//Order It
List<Player> SortedList = PlayersList.OrderByDescending(p => p.Score).ToList();
// sent fULL SORTHEDlist to the Clients
return SortedList;
}
}
}
Note that the main limitation of this sample is that it only allows a single game to be played during the life of the service. I'll leave running multiple games as an exercise for you, but will point out that you will need to track everything being done now per each game. You will likely want to bundle up the list and wait locks into objects, which you can then store another list of, in some manner such as MemoryCache