Execute multiple methods and wait for them to finish C# - c#

I have a List of objects. I need to make a call to a method of every item in that list. The method is a complex one and it takes time to complete. I am searching a way to call every method in a different thread and then wait for all to finish.
Here is my actual approach (is not working right now):
List<Task> TaskList = new List<Task>();
foreach (ComplexObject co in population)
{
TaskList.Add(Task.Run(() => co.ComplexTask()));
}
Task.WaitAll(TaskList.ToArray());
Right now I'am getting exceptions in the ComplexTask:
I can execute the ComplexMethod synchronously without errors.
I assume that I'm making mistakes and I need help with the task/await usage.
Thanks.
Edit:
ComplexTask:
public int ComplexTask()
{
int maquinaCubierta = 100;
int maquinasNoCubierta = -100;
int empleadoHabitual = 50;
int empleadoNoHabitual = 25;
int empleadoAVarios = 15;
int empleadoCambioTurno = -75;
int ett = -10;
fitness = 0;
bool perfecto = true;
procesarCalendario();
for (int dia = 0; dia < 5; dia++)
{
for (int turno = 0; turno < 3; turno++)
{
foreach (String key in asignaciones[dia][turno].Keys)
{
if (asignaciones[dia][turno][key] == null)
{
if (puestosSemana[dia][turno][0].Contains(key))
{
fitness += maquinaCubierta + ett;
}
else
{
fitness += maquinasNoCubierta;
perfecto = false;
}
}
else if (asignaciones[dia][turno][key].kPuesto == key)
{
fitness += maquinaCubierta;
fitness += asignaciones[dia][turno][key].kPuesto == key ? empleadoHabitual : empleadoNoHabitual;
if (asignaciones[dia][turno][key].turno != turno + 1)
{
fitness += empleadoCambioTurno;
perfecto = false;
}
}
}
fitness += empleadoAVarios * empleadosSinAsignacion[dia][turno].Count;
}
}
if(perfecto)
{
fitness = int.MaxValue;
}
return fitness;
}

Perhaps make fitness a parameter, however, if this is an iterative process it can't be run in parallel/concurrently
I was effectively using a shared connection. I'm starting to use unique connections and all seems to work fine now.

Related

Trying to assign a large workload into a thread pool (Unity)

I have a very specific and demanding workload I am trying to multithreaded. This is very new to me so I am struggling to find an effective solution.
Program Description: UpdateEquations() is cycling through a list of mathematical functions to update the coordinates of rendered lines. By default, func.Count = 3, so this will call CordCalc() 1500 times every frame. I am using NClac to parse a function string and write the result to the Function list, which will later be used before the end of the frame (irrelevant).
Goal: I want to put each cycle of the for(int a) loop inside its own thread. Since for(int a) will only loop 3 times, I just need to start three threads. I cannot continue the for(int i) loop until for(int a) is fully calculated. I am calculating a very large about of small tasks so it would be too expensive to assign each task to the thread.
What I am currently trying to do: I am trying to use a ThreadPool queue, however I'm not sure how to wait for them all to finish before continuing onto the next for(int i) iteration. Furthermore, while the program compiles and executes, the performance is disastrous. Probably %5 of my original performance. I am not sure if creating a "new WaitCallback" is expensive or not. I was looking for a way to predefined threads somehow so that I don't have to reinitialize them 1500 times a frame. (Which is what I suspect the issue is).
Other things I've tried: I tried using new Thread(() => CordCalc(a, i)); however this seemed to have much worse performance. I saw online somewhere that using a ThreadPool would be less expensive.
(This code is shortened for readability and relevance)
public List<Function> func;
private Expression[] exp;
private int lines_i;
private int lines_a;
public void Start()
{
func = new List<Function>();
exp = new Expression[func.Count];
for (int i = 0; i < func.Count; i++) exp[i] = new Expression(func[i].function);
}
//Calculate
public void CordCalc(object state)
{
for (int b = 0; b < func.Count; b++)
exp[lines_a].Parameters[func[b].name] = func[b].mainCords[lines_i - 1];
exp[lines_a].Parameters["t"] = t;
try
{
func[lines_a].mainCords[lines_i] = Convert.ToSingle(exp[lines_a].Evaluate());
}
catch
{
Debug.Log("input Error");
func[lines_a].mainCords[lines_i] = 0;
}
}
private void UpdateEquations()
{
//Initialize equations
for (int a = 0; a < func.Count; a++)
{
func[a].mainCords[0] = t;
}
lines_i = 1;
for (int i = 1; i < 500; i++)
{
lines_a = 0;
for (int a = 0; a < func.Count; a++)
{
//Calculate
ThreadPool.QueueUserWorkItem(new WaitCallback(CordCalc));
//This was something else that I tried, which gave worse results:
//threads[a] = new Thread(() => CordCalc(a, i));
//threads[a].Start();
//t.Join();
//This was my original method call without multithreading
//func[a].mainCords[i] = CordCalc(a, i);
lines_a++;
}
lines_i++;
}
private void FixedUpdate()
{
t += step * (2 + step) * 0.05f;
UpdateEquations();
}
//Function List
public class Function
{
public string name;
public string function;
public float[] mainCords;
//Constructor
public Function(string nameIn, string funcIn)
{
name = nameIn;
function = funcIn;
}
public void SetSize(int len)
{
mainCords = new float[len];
}
}

Prevent System.StackOverflowException when recursively calling a method

I am new to c# (or coding in general) and I guess this question is really stupid and confusing (I know I'm doing it a hard way) but please help me.
I'm trying to make a minesweeper with form app. I made a 10 x 10 of buttons and if you click it, number of mines around it will be revealed. If a mine is there "F" (the first letter of "False") will appear.
There's a constructor that contains the button, x and y position, list of surrounding blocks, number of mines around it, and a boolean that indicates if there's a mine or not.
What I tried to do was to make the 8 surrounding blocks (from the list) cleared when the player clicked a block with no mine around it and if the block surrounding that block also doesn't have any mine around it, these blocks that surrounding that block will also be cleared. The method uses foreach to reveal and check the number of mines around that block. If there's no mines, same method will be applied to that block (calling the method recursively). The problem is that I keep getting System.StackOverflowException.
I somehow understand why it's happening but I just can't come up with the other way.
//scroll to the bottom for the method with the problem
private void Form1_Load(object sender, EventArgs e)
{
Random random = new Random();
Button[,] buttons = new Button[10, 10]
{
{ r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r0c7, r0c8, r0c9 },
{ r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, r1c7, r1c8, r1c9 },
{ r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, r2c6, r2c7, r2c8, r2c9 },
{ r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, r3c7, r3c8, r3c9 },
{ r4c0, r4c1, r4c2, r4c3, r4c4, r4c5, r4c6, r4c7, r4c8, r4c9 },
{ r5c0, r5c1, r5c2, r5c3, r5c4, r5c5, r5c6, r5c7, r5c8, r5c9 },
{ r6c0, r6c1, r6c2, r6c3, r6c4, r6c5, r6c6, r6c7, r6c8, r6c9 },
{ r7c0, r7c1, r7c2, r7c3, r7c4, r7c5, r7c6, r7c7, r7c8, r7c9 },
{ r8c0, r8c1, r8c2, r8c3, r8c4, r8c5, r8c6, r8c7, r8c8, r8c9 },
{ r9c0, r9c1, r9c2, r9c3, r9c4, r9c5, r9c6, r9c7, r9c8, r9c9 }
};
Square[,] squares = new Square[10, 10];
for (int i = 0, ii = 0, iii = 0; i < 100; i++, ii++)
{
if (ii == 10)
{
ii = 0;
iii++;
}
squares[ii, iii] = new Square(i, buttons[ii, iii], ii, iii, 0, true);
}
List<int> randoms = new List<int>();
for (int i = 0; i < 10; i++)
{
int ii = random.Next(100);
if (!randoms.Contains(ii))
{
squares[ii % 10, ii / 10].setSafe(false);
}
else
{
i--;
}
randoms.Add(ii);
}
for (int i = 0; i < 10; i++)
{
for (int ii = 0; ii < 10; ii++)
{
for (int iii = -1; iii < 2; iii++)
{
for (int iiii = -1; iiii < 2; iiii++)
{
try
{
if (squares[i + iii, ii + iiii].getSafe() == false)
squares[i, ii].addNumber();
}
catch (System.IndexOutOfRangeException)
{
}
}
//if (squares[i, ii].getSafe() == false) squares[i, ii].getButton().Text = squares[i, ii].getSafe().ToString();
//else squares[i, ii].getButton().Text = squares[i, ii].getNumber().ToString();
}
}
}
for (int i = 0; i < 10; i++)
{
for (int ii = 0; ii < 10; ii++)
{
for (int iii = -1; iii < 2; iii++)
{
for (int iiii = -1; iiii < 2; iiii++)
{
try
{
squares[i, ii].addList(squares[i + iii, ii + iiii]);
}
catch (System.IndexOutOfRangeException)
{
}
}
}
}
}
}
Here's the Square class:
public class Square
{
int id;
Button button;
int x;
int y;
int number;
bool safe;
List<Square> list = new List<Square>();
public Square(int id, Button button, int x, int y, int number, bool safe)
{
this.id = id;
this.button = button;
this.x = x;
this.y = y;
this.number = number;
this.safe = safe;
button.Text = "";
button.Click += button_Click;
}
public int getId()
{
return id;
}
public void setId(int i)
{
id = i;
}
public Button getButton()
{
return button;
}
public void setButton(Button b)
{
button = b;
}
public int getX()
{
return x;
}
public void setX(int i)
{
x = i;
}
public int getY()
{
return y;
}
public void setY(int i)
{
y = i;
}
public int getNumber()
{
return number;
}
public void setNumber(int i)
{
number = i;
}
public void addNumber()
{
number++;
}
public bool getSafe()
{
return safe;
}
public void setSafe(bool b)
{
safe = b;
}
private void button_Click(object sender, EventArgs e)
{
if (getSafe() == false) button.Text = getSafe().ToString();
else button.Text = getNumber().ToString();
if (getNumber() == 0) zeroReveal();
}
//---------------------------------------------------
// this is the method that reveals surrounding blocks
//---------------------------------------------------
private void zeroReveal()
{
foreach (Square s in list)
{
//revealing the blocks
s.getButton().Text = s.getNumber().ToString();
//call the same method if there's no mine
//this is the line that keeps giving me exception
if (s.getNumber() == 0) s.zeroReveal();
}
}
//-----------------------------------------------------
public List<Square> getList()
{
return list;
}
public void setList(List<Square> sl)
{
list = sl;
}
public void addList(Square s)
{
list.Add(s);
}
}
I am new to c# (or coding in general) and I guess this question is really stupid and confusing (I know I'm doing it a hard way)
This topic confuses many a new developer; don't stress out about it!
If there's no mines, same method will be applied to that block (calling the method recursively).
Recursive methods can be confusing but if you design them using the standard pattern, you will avoid SO exceptions. You have not designed yours using the standard pattern.
The standard pattern for successful recursive methods is:
Am I in a case that requires no recursion?
If yes, do the necessary computations to produce the desired effect and return. The problem is now solved.
If no, then we're going to recurse.
Break the current problem down into some number of smaller problems.
Solve each smaller problem by recursing.
Combine the solutions of the smaller problem to solve the current problem.
The problem is now solved, so return.
The most important thing about designing a recursive method is that each recursion must be solving a smaller problem, and the sequence of smaller problems must bottom out at a case that does not require recursion. If those two conditions are not met, then you will get a stack overflow.
Internalize that pattern, and every time you write a recursive method, actually write it out:
int Frob(int blah)
{
if (I am in the base case)
{
solve the base case
return the result
}
else
{
find smaller problems
solve them
combine their solutions
return the result
}
}
Fill in that template with your real code, and you will be much more likely to avoid stack overflows. I've been writing recursive methods for decades, and I still follow this pattern.
Now, in your example, what is the case that does not require recursion? There must be one, so write down what it is. Next, how will you guarantee that the recursion solves a smaller problem? That is often the hard step! Give it some thought.
The stack overflow is occurring because zeroReveal is recursively calling itself forever. To fix this we need to find ways where we do not need it to make further calls to itself.
The name of the method gives us a clue. If the square has already been revealed, then surely the method does not need to do anything, since it has already been revealed.
It looks like the button's Text property is an empty string if it has not yet been revealed. So change the foreach so that it doesn't process squares that have already been revealed:
foreach (Square s in list)
{
if (s.getButton().Text == ""))
{
// existing code in the foreach loop goes here
}
}

c# comparing list of IDs

I have a List<Keyword> where Keyword class is:
public string keyword;
public List<int> ids;
public int hidden;
public int live;
public bool worked;
Keyword has its own keyword, a set of 20 ids, live by default is set to 1 and hidden to 0.
I just need to iterate over the whole main List to invalidate those keywords whose number of same ids is greater than 6, so comparing every pair, if the second one has more than 6 ids repeated respect to the first one, hidden is set to 1 and live to 0.
The algorithm is very basic but it takes too long when the main list has many elements.
I'm trying to guess if there could be any method I could use to increase the speed.
The basic algorithm I use is:
foreach (Keyword main_keyword in lista_de_keywords_live)
{
if (main_keyword.worked) {
continue;
}
foreach (Keyword keyword_to_compare in lista_de_keywords_live)
{
if (keyword_to_compare.worked || keyword_to_compare.id == main_keyword.id) continue;
n_ids_same = 0;
foreach (int id in main_keyword.ids)
{
if (keyword_to_compare._lista_models.IndexOf(id) >= 0)
{
if (++n_ids_same >= 6) break;
}
}
if (n_ids_same >= 6)
{
keyword_to_compare.hidden = 1;
keyword_to_compare.live = 0;
keyword_to_compare.worked = true;
}
}
}
The code below is an example of how you would use a HashSet for your problem. However, I would not recommend using it in this scenario. On the other hand, the idea of sorting the ids to make the comparison faster still.
Run it in a Console Project to try it out.
Notice that once I'm done adding new ids to a keyword, I sort them. This makes the comparison faster later on.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace KeywordExample
{
public class Keyword
{
public List<int> ids;
public int hidden;
public int live;
public bool worked;
public Keyword()
{
ids = new List<int>();
hidden = 0;
live = 1;
worked = false;
}
public override string ToString()
{
StringBuilder s = new StringBuilder();
if (ids.Count > 0)
{
s.Append(ids[0]);
for (int i = 1; i < ids.Count; i++)
{
s.Append(',' + ids[i].ToString());
}
}
return s.ToString();
}
}
public class KeywordComparer : EqualityComparer<Keyword>
{
public override bool Equals(Keyword k1, Keyword k2)
{
int equals = 0;
int i = 0;
int j = 0;
//based on sorted ids
while (i < k1.ids.Count && j < k2.ids.Count)
{
if (k1.ids[i] < k2.ids[j])
{
i++;
}
else if (k1.ids[i] > k2.ids[j])
{
j++;
}
else
{
equals++;
i++;
j++;
}
}
return equals >= 6;
}
public override int GetHashCode(Keyword keyword)
{
return 0;//notice that using the same hash for all keywords gives you an O(n^2) time complexity though.
}
}
class Program
{
static void Main(string[] args)
{
List<Keyword> listOfKeywordsLive = new List<Keyword>();
//add some values
Random random = new Random();
int n = 10;
int sizeOfMaxId = 20;
for (int i = 0; i < n; i++)
{
var newKeyword = new Keyword();
for (int j = 0; j < 20; j++)
{
newKeyword.ids.Add(random.Next(sizeOfMaxId) + 1);
}
newKeyword.ids.Sort(); //sorting the ids
listOfKeywordsLive.Add(newKeyword);
}
//solution here
HashSet<Keyword> set = new HashSet<Keyword>(new KeywordComparer());
set.Add(listOfKeywordsLive[0]);
for (int i = 1; i < listOfKeywordsLive.Count; i++)
{
Keyword keywordToCompare = listOfKeywordsLive[i];
if (!set.Add(keywordToCompare))
{
keywordToCompare.hidden = 1;
keywordToCompare.live = 0;
keywordToCompare.worked = true;
}
}
//print all keywords to check
Console.WriteLine(set.Count + "/" + n + " inserted");
foreach (var keyword in set)
{
Console.WriteLine(keyword);
}
}
}
}
The obvious source of inefficiency is the way you calculate intersection of two lists (of ids). The algorithm is O(n^2). This is by the way problem that relational databases solve for every join and your approach would be called loop join. The main efficient strategies are hash join and merge join. For your scenario the latter approach may be better I guess, but you can also try HashSets if you like.
The second source of inefficiency is repeating everything twice. As (a join b) is equal to (b join a), you do not need two cycles over the whole List<Keyword>. Actually, you only need to loop over the non duplicate ones.
Using some code from here, you can write the algorithm like:
Parallel.ForEach(list, k => k.ids.Sort());
List<Keyword> result = new List<Keyword>();
foreach (var k in list)
{
if (result.Any(r => r.ids.IntersectSorted(k.ids, Comparer<int>.Default)
.Skip(5)
.Any()))
{
k.hidden = 1;
k.live = 0;
k.worked = true;
}
else
{
result.Add(k);
}
}
If you replace the linq with just the index manipulation approach (see the link above), it would be a tiny bit faster I guess.

(Dynamic programming) How to maximize room utilization with a list of meeting?

I am trying this problem using dynamic programming
Problem:
Given a meeting room and a list of intervals (represent the meeting), for e.g.:
interval 1: 1.00-2.00
interval 2: 2.00-4.00
interval 3: 14.00-16.00
...
etc.
Question:
How to schedule the meeting to maximize the room utilization, and NO meeting should overlap with each other?
Attempted solution
Below is my initial attempt in C# (knowing it is a modified Knapsack problem with constraints). However I had difficulty in getting the result correctly.
bool ContainsOverlapped(List<Interval> list)
{
var sortedList = list.OrderBy(x => x.Start).ToList();
for (int i = 0; i < sortedList.Count; i++)
{
for (int j = i + 1; j < sortedList.Count; j++)
{
if (sortedList[i].IsOverlap(sortedList[j]))
return true;
}
}
return false;
}
public bool Optimize(List<Interval> intervals, int limit, List<Interval> itemSoFar){
if (intervals == null || intervals.Count == 0)
return true; //no more choice
if (Sum(itemSoFar) > limit) //over limit
return false;
var arrInterval = intervals.ToArray();
//try all choices
for (int i = 0; i < arrInterval.Length; i++){
List<Interval> remaining = new List<Interval>();
for (int j = i + 1; j < arrInterval.Length; j++) {
remaining.Add(arrInterval[j]);
}
var partialChoice = new List<Interval>();
partialChoice.AddRange(itemSoFar);
partialChoice.Add(arrInterval[i]);
//should not schedule overlap
if (ContainsOverlapped(partialChoice))
partialChoice.Remove(arrInterval[i]);
if (Optimize(remaining, limit, partialChoice))
return true;
else
partialChoice.Remove(arrInterval[i]); //undo
}
//try all solution
return false;
}
public class Interval
{
public bool IsOverlap(Interval other)
{
return (other.Start < this.Start && this.Start < other.End) || //other < this
(this.Start < other.Start && other.End < this.End) || // this covers other
(other.Start < this.Start && this.End < other.End) || // other covers this
(this.Start < other.Start && other.Start < this.End); //this < other
}
public override bool Equals(object obj){
var i = (Interval)obj;
return base.Equals(obj) && i.Start == this.Start && i.End == this.End;
}
public int Start { get; set; }
public int End { get; set; }
public Interval(int start, int end){
Start = start;
End = end;
}
public int Duration{
get{
return End - Start;
}
}
}
Edit 1
Room utilization = amount of time the room is occupied. Sorry for confusion.
Edit 2
for simplicity: the duration of each interval is integer, and the start/end time start at whole hour (1,2,3..24)
I'm not sure how you are relating this to a knapsack problem. To me it seems more of a vertex cover problem.
First sort the intervals as per their start times and form a graph representation in the form of adjacency matrix or list.
The vertices shall be the interval numbers. There shall be an edge between two vertices if the corresponding intervals overlap with each other. Also, each vertex shall be associated with a value equal to the interval's duration.
The problem then becomes choosing the independent vertices in such a way that the total value is maximum.
This can be done through dynamic programming. The recurrence relation for each vertex shall be as follows:
V[i] = max{ V[j] | j < i and i->j is an edge,
V[k] + value[i] | k < i and there is no edge between i and k }
Base Case V[1] = value[1]
Note:
The vertices should be numbered in increasing order of their start times. Then if there are three vertices:
i < j < k, and if there is no edge between vertex i and vertex j, then there cannot be any edge between vertex i and vertex k.
Good approach is to create class that can easily handle for you.
First I create helper class for easily storing intervals
public class FromToDateTime
{
private DateTime _start;
public DateTime Start
{
get
{
return _start;
}
set
{
_start = value;
}
}
private DateTime _end;
public DateTime End
{
get
{
return _end;
}
set
{
_end = value;
}
}
public FromToDateTime(DateTime start, DateTime end)
{
Start = start;
End = end;
}
}
And then here is class Room, where all intervals are and which has method "addInterval", which returns true, if interval is ok and was added and false, if it does not.
btw : I got a checking condition for overlapping here : Algorithm to detect overlapping periods
public class Room
{
private List<FromToDateTime> _intervals;
public List<FromToDateTime> Intervals
{
get
{
return _intervals;
}
set
{
_intervals = value;
}
}
public Room()
{
Intervals = new List<FromToDateTime>();
}
public bool addInterval(FromToDateTime newInterval)
{
foreach (FromToDateTime interval in Intervals)
{
if (newInterval.Start < interval.End && interval.Start < newInterval.End)
{
return false;
}
}
Intervals.Add(newInterval);
return true;
}
}
While the more general problem (if you have multiple number of meeting rooms) is indeed NP-Hard, and is known as the interval scheduling problem.
Optimal solution for 1-d problem with one classroom:
For the 1-d problem, choosing the (still valid) earliest deadline first solves the problem optimally.
Proof: by induction, the base clause is the void clause - the algorithm optimally solves a problem with zero meetings.
The induction hypothesis is the algorithm solves the problem optimally for any number of k tasks.
The step: Given a problem with n meetings, hose the earliest deadline, and remove all invalid meetings after choosing it. Let the chosen earliest deadline task be T.
You will get a new problem of smaller size, and by invoking the algorithm on the reminder, you will get the optimal solution for them (induction hypothesis).
Now, note that given that optimal solution, you can add at most one of the discarded tasks, since you can either add T, or another discarded task - but all of them overlaps T - otherwise they wouldn't have been discarded), thus, you can add at most one from all discarded tasks, same as the suggested algorithm.
Conclusion: For 1 meeting room, this algorithm is optimal.
QED
high level pseudo code of the solution:
findOptimal(list<tasks>):
res = [] //empty list
sort(list) //according to deadline/meeting end
while (list.IsEmpty() == false):
res = res.append(list.first())
end = list.first().endTime()
//remove all overlaps with the chosen meeting
while (list.first().startTine() < end):
list.removeFirst()
return res
Clarification: This answer assumes "Room Utilization" means maximize number of meetings placed in the room.
Thanks all, here is my solution based on this Princeton note on dynamic programming.
Algorithm:
Sort all events by end time.
For each event, find p[n] - the latest event (by end time) which does not overlap with it.
Compute the optimization values: choose the best between including/not including the event.
Optimize(n) {
opt(0) = 0;
for j = 1 to n-th {
opt(j) = max(length(j) + opt[p(j)], opt[j-1]);
}
}
The complete source-code:
namespace CommonProblems.Algorithm.DynamicProgramming {
public class Scheduler {
#region init & test
public List<Event> _events { get; set; }
public List<Event> Init() {
if (_events == null) {
_events = new List<Event>();
_events.Add(new Event(8, 11));
_events.Add(new Event(6, 10));
_events.Add(new Event(5, 9));
_events.Add(new Event(3, 8));
_events.Add(new Event(4, 7));
_events.Add(new Event(0, 6));
_events.Add(new Event(3, 5));
_events.Add(new Event(1, 4));
}
return _events;
}
public void DemoOptimize() {
this.Init();
this.DynamicOptimize(this._events);
}
#endregion
#region Dynamic Programming
public void DynamicOptimize(List<Event> events) {
events.Add(new Event(0, 0));
events = events.SortByEndTime();
int[] eventIndexes = getCompatibleEvent(events);
int[] utilization = getBestUtilization(events, eventIndexes);
List<Event> schedule = getOptimizeSchedule(events, events.Count - 1, utilization, eventIndexes);
foreach (var e in schedule) {
Console.WriteLine("Event: [{0}- {1}]", e.Start, e.End);
}
}
/*
Algo to get optimization value:
1) Sort all events by end time, give each of the an index.
2) For each event, find p[n] - the latest event (by end time) which does not overlap with it.
3) Compute the optimization values: choose the best between including/not including the event.
Optimize(n) {
opt(0) = 0;
for j = 1 to n-th {
opt(j) = max(length(j) + opt[p(j)], opt[j-1]);
}
display opt();
}
*/
int[] getBestUtilization(List<Event> sortedEvents, int[] compatibleEvents) {
int[] optimal = new int[sortedEvents.Count];
int n = optimal.Length;
optimal[0] = 0;
for (int j = 1; j < n; j++) {
var thisEvent = sortedEvents[j];
//pick between 2 choices:
optimal[j] = Math.Max(thisEvent.Duration + optimal[compatibleEvents[j]], //Include this event
optimal[j - 1]); //Not include
}
return optimal;
}
/*
Show the optimized events:
sortedEvents: events sorted by end time.
index: event index to start with.
optimal: optimal[n] = the optimized schedule at n-th event.
compatibleEvents: compatibleEvents[n] = the latest event before n-th
*/
List<Event> getOptimizeSchedule(List<Event> sortedEvents, int index, int[] optimal, int[] compatibleEvents) {
List<Event> output = new List<Event>();
if (index == 0) {
//base case: no more event
return output;
}
//it's better to choose this event
else if (sortedEvents[index].Duration + optimal[compatibleEvents[index]] >= optimal[index]) {
output.Add(sortedEvents[index]);
//recursive go back
output.AddRange(getOptimizeSchedule(sortedEvents, compatibleEvents[index], optimal, compatibleEvents));
return output;
}
//it's better NOT choose this event
else {
output.AddRange(getOptimizeSchedule(sortedEvents, index - 1, optimal, compatibleEvents));
return output;
}
}
//compatibleEvents[n] = the latest event which do not overlap with n-th.
int[] getCompatibleEvent(List<Event> sortedEvents) {
int[] compatibleEvents = new int[sortedEvents.Count];
for (int i = 0; i < sortedEvents.Count; i++) {
for (int j = 0; j <= i; j++) {
if (!sortedEvents[j].IsOverlap(sortedEvents[i])) {
compatibleEvents[i] = j;
}
}
}
return compatibleEvents;
}
#endregion
}
public class Event {
public int EventId { get; set; }
public bool IsOverlap(Event other) {
return !(this.End <= other.Start ||
this.Start >= other.End);
}
public override bool Equals(object obj) {
var i = (Event)obj;
return base.Equals(obj) && i.Start == this.Start && i.End == this.End;
}
public int Start { get; set; }
public int End { get; set; }
public Event(int start, int end) {
Start = start;
End = end;
}
public int Duration {
get {
return End - Start;
}
}
}
public static class ListExtension {
public static bool ContainsOverlapped(this List<Event> list) {
var sortedList = list.OrderBy(x => x.Start).ToList();
for (int i = 0; i < sortedList.Count; i++) {
for (int j = i + 1; j < sortedList.Count; j++) {
if (sortedList[i].IsOverlap(sortedList[j]))
return true;
}
}
return false;
}
public static List<Event> SortByEndTime(this List<Event> events) {
if (events == null) return new List<Event>();
return events.OrderBy(x => x.End).ToList();
}
}
}

Multithread access to list C#

I have a task to show difference between syncronized and unsyncronized multithreading. Therefore I wrote an application simulating withdrawing money from clients' bank accounts. Each of some number of threads chooses a random user and withdraws money from the account.
Every thread should withdraw every account once. First time the threads are syncronized, but the second time they are not. So there must be a difference between accounts, withdrawed by syncronized and unsyncronized threads. And the difference must be different for different numbers of users and threads. But in my application I have difference just for 1000 threads. So I need unsyncronized threads' results to be strongly different from syncronized threads' ones.
The class User:
public class User : IComparable
{
public string Name { get; set; }
public int Start { get; set; }
public int FinishSync { get; set; }
public int FinishUnsync { get; set; }
public int Hypothetic { get; set; }
public int Differrence { get; set; }
...
}
The method which withdraws money:
public void Withdraw(ref List<User> users, int sum, bool isSync)
{
int ind = 0;
Thread.Sleep(_due);
var rnd = new Random(DateTime.Now.Millisecond);
//used is list of users, withrawed by the thread
while (_used.Count < users.Count)
{
while (_used.Contains(ind = rnd.Next(0, users.Count))) ; //choosing a random user
if (isSync) //isSync = if threads syncroized
{
if (Monitor.TryEnter(users[ind]))
{
try
{
users[ind].FinishSync = users[ind].FinishSync - sum;
}
finally
{
Monitor.Exit(users[ind]);
}
}
}
else
{
lock (users[ind])
{
users[ind].FinishUnsync = users[ind].FinishUnsync - sum;
}
}
_used.Add(ind);
}
done = true;
}
And the threads are created this way:
private void Withdrawing(bool IsSync)
{
if (IsSync)
{
for (int i = 0; i < _num; i++)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, true); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
_threads[i].Join();
}
}
else
{
for (int i = 0; i < _num; ++i)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, false); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
}
}
I've changed the Withdraw class this way, bc the problem could have been in creating threads separately from the delegate:
class Withdrawer
{
private List<int>[] _used;
private int _due;
private int _pause;
public int done;
private List<Thread> _threads;
public Withdrawer(List<User> users, int n, int due, int pause, int sum)
{
_due = due;
_pause = pause;
done = 0;
_threads = new List<Thread>(users.Count);
InitializeUsed(users, n);
CreateThreads(users, n, sum, false);
_threads.Clear();
while (done < n) ;
Array.Clear(_used,0,n-1);
InitializeUsed(users, n);
CreateThreads(users, n, sum, true);
}
private void InitializeUsed(List<User> users, int n)
{
_used = new List<int>[n];
for (int i = 0; i < n; i++)
{
_used[i] = new List<int>(users.Count);
for (int j = 0; j < users.Count; j++)
{
_used[i].Add(j);
}
}
}
private void CreateThreads(List<User> users, int n, int sum, bool isSync)
{
for (int i = 0; i < n; i++)
{
_threads.Add(new Thread(delegate() { Withdraw(users, sum, isSync); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
}
public void Withdraw(List<User> users, int sum, bool isSync)
{
int ind = 0;
var rnd = new Random();
while (_used[int.Parse(Thread.CurrentThread.Name)].Count > 0)
{
int x = rnd.Next(_used[int.Parse(Thread.CurrentThread.Name)].Count);
ind = _used[int.Parse(Thread.CurrentThread.Name)][x];
if (isSync)
{
lock (users[ind])
{
Thread.Sleep(_due);
users[ind].FinishSync -= sum;
}
}
else
{
Thread.Sleep(_due);
users[ind].FinishUnsync -= sum;
}
_used[int.Parse(Thread.CurrentThread.Name)][x] = _used[int.Parse(Thread.CurrentThread.Name)][_used[int.Parse(Thread.CurrentThread.Name)].Count - 1];
_used[int.Parse(Thread.CurrentThread.Name)].RemoveAt(_used[int.Parse(Thread.CurrentThread.Name)].Count - 1);
Thread.Sleep(_pause);
}
done++;
}
}
Now the problem is FinishUnSync values are correct, while FinishSync values are absolutely not.
Thread.Sleep(_due);
and
Thread.Sleep(_pause);
are used to "hold" the resourse, bc my task is the thread should get resourse, hold it for a _due ms, and after processing wait _pause ms before finishing.
Your code isn't doing anything useful, and doesn't show the difference between synchronized and unsynchronized access. There are many things you'll need to address.
Comments in your code say that _used is a list of users that have been accessed by the thread. You're apparently creating that on a per-thread basis. If that's true, I don't see how. From the looks of things I'd say that _used is accessible to all threads. I don't see anywhere that you're creating a per-thread version of that list. And the naming convention indicates that it's at class scope.
If that list is not per-thread, that would go a long way towards explaining why your data is always the same. You also have a real race condition here because you're updating the list from multiple threads.
Assuning that _used really is a per-thread data structure . . .
You have this code:
if (isSync) //isSync = if threads syncroized
{
if (Monitor.TryEnter(users[ind]))
{
try
{
users[ind].FinishSync = users[ind].FinishSync - sum;
}
finally
{
Monitor.Exit(users[ind]);
}
}
}
else
{
lock (users[ind])
{
users[ind].FinishUnsync = users[ind].FinishUnsync - sum;
}
}
Both of these provide synchronization. In the isSync case, a second thread will fail to do its update if a thread already has the user locked. In the second case, the second thread will wait for the first to finish, and then will do the update. In either case, the use of Monitor or lock prevents concurrent update.
Still, you would potentially see a difference if multiple threads could be executing the isSync code at the same time. But you won't see a difference because in your synchronized case you never let more than one thread execute. That is, you have:
if (IsSync)
{
for (int i = 0; i < _num; i++)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, true); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
_threads[i].Join();
}
}
else
{
for (int i = 0; i < _num; ++i)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, false); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
}
So in the IsSync case, you start a thread and then wait for it to complete before you start another thread. Your code is not multithreaded. And in the "unsynchronized" case you're using a lock to prevent concurrent updates. So in one case you prevent concurrent updates by only running one thread at a time, and in the other case you prevent concurrent updates by using a lock. There will be no difference.
Something else worth noting is that your method of randomly selecting a user is highly inefficient, and could be part of the problem you're seeing. Basically what you're doing is picking a random number and checking to see if it's in a list. If it is, you try again, etc. And the list keeps growing. Quick experimentation shows that I have to generate 7,000 random numbers between 0 and 1,000 before I get all of them. So your threads are spending a huge amount of time trying to find the next unused account, meaning that they have less likelihood to be processing the same user account at the same time.
You need to do three things. First, change your Withdrawl method so it does this:
if (isSync) //isSync = if threads syncroized
{
// synchronized. prevent concurrent updates.
lock (users[ind])
{
users[ind].FinishSync = users[ind].FinishSync - sum;
}
}
else
{
// unsynchronized. It's a free-for-all.
users[ind].FinishUnsync = users[ind].FinishUnsync - sum;
}
Your Withdrawing method should be the same regardless of whether IsSync is true or not. That is, it should be:
for (int i = 0; i < _num; ++i)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, false); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
Now you always have multiple threads running. The only difference is whether access to the user account is synchronized.
Finally, make your _used list a list of indexes into the users list. Something like:
_used = new List<int>(users.Count);
for (int i = 0; i < _used.Count; ++i)
{
_used[i] = i;
}
Now, when you select a user, you do this:
var x = rnd.Next(_used.Count);
ind = _used[x];
// now remove the item from _used
_used[x] = _used[_used.Count-1];
_used.RemoveAt(_used.Count-1);
That way you can generate all users more efficiently. It will take n random numbers to generate n users.
A couple of nitpicks:
I have no idea why you have the Thread.Sleep call in the Withdraw method. What benefit do you think it provides?
There's no real reason to pass DateTime.Now.Millisecond to the Random constructor. Just calling new Random() will use Environment.TickCount for the seed. Unless you really want to limit the seed to numbers between 0 and 1,000.

Categories