Edit: After getting five downvotes but no comments saying anything about why, I've tried to reformulate the question. I can only assume the votes are because people see a lot of text and maybe think there isn't even a question in here.
I've written code that (mis)behaves rather strangely. It seems the code doesn't run the same way on other's computers, so please don't get mad at me if you can't reproduce the problem.
I had a look, just for fun, at the frequencies with which different bytes occur in GUIDs. I had noticed that the string representations of guids always contained "4". Rather than read about it on Wikipedia I tried to think about what it might be, as it can be fun to do a little "original research" and do your own thinking once in a while. (And then read the wiki afterwards!)
The strange issue on my machine happens when I try to use the "burst" feature. Provided it is reproducible in your environment, you can re-create it by running the app and hitting B. This is supposed to result in a burst of 100 steps (i.e. make 100 new guids, updating the displayed frequencies only at the end of the burst, because writing to the console is so ridiculously slow). But it actually results in just a single step!
I set a breakpoint in ProcessKey() where the burst variable is assigned. When I step out of the method, I notice in Debug Output that a thread exits. This is not a coincidence; it happens reliably every time. Then my watches show me the burst variable that was just assigned to 1000 in the previous step... has value 0.
Why does this happen? Did I do something wrong? I notice there is no attribute anywhere specifying STA, but I've never really had a clue what these things are anyway and I haven't removed anything from the console application template I used (with VS-2011 developer preview, though unlike Redmond I live in 2012 now)...
Finally: The app moves the cursor back and overwrites the text again and again. This doesn't work well if you can't make the console window high enough to show all the output at once, so you may want to fiddle with the console font (or change the width of the console, the app should change accordingly though I haven't tested). The pattern becomes completely regular (on my machine at least!) with a 4-column output (if your console is 80 chars wide you'll get 4 columns).
Code to reproduce (or not, as the case may be):
using System;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static bool running, exit;
static int burst;
static long guidCount = 0;
static long[] counts = new long[256];
static DateTime nextReport = DateTime.MinValue;
static readonly TimeSpan reportInterval = TimeSpan.FromSeconds(0.25);
static void Main(string[] args)
{
Console.WindowHeight = (int)(0.8*Console.LargestWindowHeight);
WriteLine(ConsoleColor.White, "X - Exit | P - Pause | S - Step (hold for Slow) | B - Burst\r\n");
WriteLine("Auto-reporting interval = {0}.", reportInterval);
Guid guid;
byte[] bytes;
var cursorPos = new CursorLocation();
while (!exit)
{
if (Console.KeyAvailable)
{
ProcessKey(Console.ReadKey(true));
}
if (running || burst > 0)
{
guid = Guid.NewGuid();
bytes = guid.ToByteArray();
++guidCount;
for (int i = 0; i < 16; i++)
{
var b = bytes[i];
++counts[b];
}
if (burst > 0) --burst;
if (burst == 0 || DateTime.Now > nextReport)
{
burst = -1;
cursorPos.MoveCursor();
ReportFrequencies();
}
}
else
Thread.Sleep(20);
}
}
static void ProcessKey(ConsoleKeyInfo keyInfo)
{
switch (keyInfo.Key)
{
case ConsoleKey.P:
running = !running;
break;
case ConsoleKey.B:
burst = 100;
break;
case ConsoleKey.S:
burst = 1;
break;
case ConsoleKey.X:
exit = true;
break;
}
}
static void ReportFrequencies()
{
Write("\r\n{0} GUIDs generated. Frequencies:\r\n\r\n", guidCount);
const int itemWidth = 9;
int colCount = Console.WindowWidth / (itemWidth*2);
for (int i = 0; i < 256; i++)
{
var f = (double)counts[i] / (16 * guidCount);
Write(RightAdjust(itemWidth, "{0:x}", i));
Write(GetFrequencyColor(f), " {0:p}".PadRight(itemWidth), f);
if ((i + 1) % colCount == 0) Write("\r\n");
}
nextReport = DateTime.Now + reportInterval;
}
static ConsoleColor GetFrequencyColor(double f)
{
if (f < 0.003) return ConsoleColor.DarkRed;
if (f < 0.004) return ConsoleColor.Green;
if (f < 0.005) return ConsoleColor.Yellow;
return ConsoleColor.White;
}
static string RightAdjust(int w, string s, params object[] args)
{
if (args.Length > 0)
s = string.Format(s, args);
return s.PadLeft(w);
}
#region From my library, so I need not include that here...
class CursorLocation
{
public int X, Y;
public CursorLocation()
{
X = Console.CursorLeft;
Y = Console.CursorTop;
}
public void MoveCursor()
{
Console.CursorLeft = X;
Console.CursorTop = Y;
}
}
static public void Write(string s, params object[] args)
{
if (args.Length > 0) s = string.Format(s, args);
Console.Write(s);
}
static public void Write(ConsoleColor c, string s, params object[] args)
{
var old = Console.ForegroundColor;
Console.ForegroundColor = c;
Write(s, args);
Console.ForegroundColor = old;
}
static public void WriteNewline(int count = 1)
{
while (count-- > 0) Console.WriteLine();
}
static public void WriteLine(string s, params object[] args)
{
Write(s, args);
Console.Write(Environment.NewLine);
}
static public void WriteLine(ConsoleColor c, string s, params object[] args)
{
Write(c, s, args);
Console.Write(Environment.NewLine);
}
#endregion
}
}
Ehem. I tried running the code at my HTPC, a different computer from the one I coded this on, and now I cannot reproduce the problem. That is, I do observe the burst leading to just a step, but that is due to a logical error in my code (when the report interval is reached it sets burst to -1). It's hard to believe I did not set my breakpoint, stepped through, and saw the variable get destroyed, because I know how weird that would be and tried several times to be sure I saw what I thought I saw. But it's also hard to believe I had stumbled upon such a weird and deep bug in the framework/clr, especially considering that my code had a bug that causes the thing that got me attaching the debugger in the first place..
In any case, I'll mark it as closed. And post the revised code here if anyone wants to play with it. I've fixed the bug and made the output a bit more compact so it works better on less generous screens than the 22" full-HD_one I did this on. It now uses 8 columns regardless of the console width, on the probably safe assumption that most people use standard 80-char width, into which 8 columns now fit.
If anyone would care run this and post their findings (just press P to quickly get stable frequencies, the step/burst thing is for silly stuff like seeing what the distribution looks like after fewer generations). On my HTPC, I get this result:
0x00 - 0x3f 0.34%
0x40 - 0x4f 0.73%
0x50 - 0x7f 0.34%
0x80 - 0xbf 0.44%
0xc0 - 0xff 0.34%
This means: Bytes 0x00 to 0x3f each made up 0.34% of all the bytes in all the guids generated (509,194 in this particular case, but I get this result every time with more than 100,000 guids or so). There are 3 very distinct groups, and maybe if I now go and read about Guids on wikipedia I will understand why that is. But it wouldn't be as much fun to do this if my "discovery" was something I knew before I began. :)
using System;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static bool running, exit;
static int burst;
static long guidCount = 0;
static long[] counts = new long[256];
static DateTime nextReport = DateTime.MinValue;
static readonly TimeSpan reportInterval = TimeSpan.FromSeconds(1);
static void Main(string[] args)
{
Console.WindowHeight = (int)(0.8 * Console.LargestWindowHeight);
WriteLine(ConsoleColor.White, "X - Exit | P - Run/Pause | S - Step (hold for Slow) | B - Burst");
WriteLine("Press P, S or B to make something happen.", reportInterval);
Guid guid;
byte[] bytes;
var cursorPos = new CursorLocation();
while (!exit)
{
if (Console.KeyAvailable)
{
ProcessKey(Console.ReadKey(true));
}
if (running || burst > 0)
{
guid = Guid.NewGuid();
bytes = guid.ToByteArray();
++guidCount;
for (int i = 0; i < 16; i++)
{
var b = bytes[i];
++counts[b];
}
if (burst > 0) --burst;
if (burst == 0 && DateTime.Now > nextReport)
{
cursorPos.MoveCursor();
ReportFrequencies();
}
}
else
Thread.Sleep(20);
}
}
static void ProcessKey(ConsoleKeyInfo keyInfo)
{
switch (keyInfo.Key)
{
case ConsoleKey.P:
running = !running;
break;
case ConsoleKey.B:
burst = 100;
break;
case ConsoleKey.S:
burst = 1;
break;
case ConsoleKey.X:
exit = true;
break;
}
}
static void ReportFrequencies()
{
Write("\r\n{0} GUIDs generated. Frequencies (%):\r\n\r\n", guidCount);
const int itemWidth = 11;
const int colCount = 8; // Console.WindowWidth / (itemWidth + 2);
for (int i = 0; i < 256; i++)
{
var f = (double)counts[i] / (16 * guidCount);
var c = GetFrequencyColor(f);
Write(c, RightAdjust(3, "{0:x}", i));
Write(c, " {0:0.00}".PadRight(itemWidth), f*100);
if ((i + 1) % colCount == 0) Write("\r\n");
}
nextReport = DateTime.Now + reportInterval;
}
static ConsoleColor GetFrequencyColor(double f)
{
if (f < 0.003) return ConsoleColor.DarkRed;
if (f < 0.004) return ConsoleColor.Green;
if (f < 0.005) return ConsoleColor.Yellow;
return ConsoleColor.White;
}
static string RightAdjust(int w, string s, params object[] args)
{
if (args.Length > 0)
s = string.Format(s, args);
return s.PadLeft(w);
}
#region From my library, so I need not include that here...
class CursorLocation
{
public int X, Y;
public CursorLocation()
{
X = Console.CursorLeft;
Y = Console.CursorTop;
}
public void MoveCursor()
{
Console.CursorLeft = X;
Console.CursorTop = Y;
}
}
static public void Write(string s, params object[] args)
{
if (args.Length > 0) s = string.Format(s, args);
Console.Write(s);
}
static public void Write(ConsoleColor c, string s, params object[] args)
{
var old = Console.ForegroundColor;
Console.ForegroundColor = c;
Write(s, args);
Console.ForegroundColor = old;
}
static public void WriteNewline(int count = 1)
{
while (count-- > 0) Console.WriteLine();
}
static public void WriteLine(string s, params object[] args)
{
Write(s, args);
Console.Write(Environment.NewLine);
}
static public void WriteLine(ConsoleColor c, string s, params object[] args)
{
Write(c, s, args);
Console.Write(Environment.NewLine);
}
#endregion
}
}
Post your results, ladies and gentlemen. :)
Related
so we can give any number of parameters to a method, like this:
static int sumPrices(params int[] prices) {
int sum = 0;
for (int i = 0; i < prices.Length; i++) {
sum += prices[i];
}
return sum;
}
static void Main(string[] args)
{
int total = sumPrices(100, 50, 200, 350);
Console.WriteLine(total);
Console.ReadKey();
}
but ... what if we wanted to get the "100, 50, 200, 350" from the USER?
in above example it's the coder giving the sumPrices method its arguments/parameters.
i mean, it would be one solution to just pass the method an array.
static int sumPrices(int[] prices) {
int sum = 0;
for (int i = 0; i < prices.Length; i++) {
sum += prices[i];
}
return sum;
}
static void Main(string[] args)
{
//get up to 9999 inputs and sum them using a method
bool whileLoop = true;
int[] inputs = new int[9999];
int index = 0;
while (whileLoop == true)
{
Console.WriteLine("entering price #" + index + ", type 'stop' to sum prices");
string check = Console.ReadLine();
if (check == "stop") {
whileLoop = false;
break;
}
inputs[index] = int.Parse(check);
index++;
}
int[] prices = new int[index];
for (int i = 0; i < index; i++)
{
prices[i] = inputs[i];
}
Console.WriteLine("-----------------");
Console.WriteLine(sumPrices(prices));
Console.ReadKey();
}
BUT... longer code... and has limits.
our professor basically wanted the first code, however, i don't see how and where it's
supposed be used if we didn't know the user's inputs and if params arguments were coming from the coder (unless of course, coder using the same function multiple times for convenience)
i have tried to think of a solution and i'm not exactly sure how it could be done.
but it basically goes like this.
we could get inputs from the user separated by commas:
100,200,50
and the program would translate it into params arguments:
sumPrices("100,200,50");
but as you can see... it's a string. i wonder if there's a JSON.parse() thing like in js.
The code in the OP has some unnecessary code as well as some limitations such as 9999.
Let's examine the following code:
bool whileLoop = true;
while (whileLoop == true)
{
}
This could be re-written as:
while (true == true)
{
}
or
while (true)
{
}
Next, let's examine the following code:
if (check == "stop") {
whileLoop = false;
break;
}
You've set whileLoop = false; which is unnecessary because once break is executed, excution has moved to after the loop.
Since the input array has a hard-code size of 9999, you've limited yourself to 9999 values. You may consider using a List instead or resize the array.
Try the following:
using System;
using System.Collections.Generic;
using System.Linq;
namespace UserInputTest
{
internal class Program
{
static void Main(string[] args)
{
decimal price = 0;
List<decimal> _values = new List<decimal>();
if (args.Length == 0)
{
//prompts for user input
Console.WriteLine("The following program will sum the prices that are entered. To exit, type 'q'");
do
{
Console.Write("Please enter a price: ");
string userInput = Console.ReadLine();
if (userInput.ToLower() == "q")
break; //exit loop
else if (Decimal.TryParse(userInput, out price))
_values.Add(price); //if the user input can be converted to a decimal, add it to the list
else
Console.WriteLine($"Error: Invalid price ('{userInput}'). Please try again.");
} while (true);
}
else
{
//reads input from command-line arguments instead of prompting for user input
foreach (string arg in args)
{
if (Decimal.TryParse(arg, out price))
{
//if the user input can be converted to a decimal, add it to the list
_values.Add(price); //add
}
else
{
Console.WriteLine($"Error: Invalid price ('{arg}'). Exiting.");
Environment.Exit(-1);
}
}
}
Console.WriteLine($"\nThe sum is: {SumPrices(_values)}");
}
static decimal SumPrices(List<decimal> prices)
{
return prices.Sum(x => x);
}
static decimal SumPricesF(List<decimal> prices)
{
decimal sum = 0;
foreach (decimal price in prices)
sum += price;
return sum;
}
}
}
Usage 1:
UserInputTest.exe
Usage 2:
UserInputTest.exe 1 2 3 4 5
Resources:
Built-in types (C# reference)
Floating-point numeric types (C# reference)
Decimal.TryParse
Iteration statements - for, foreach, do, and while
String interpolation using $
List Class
Enumerable.Sum Method
Default values of C# types (C# reference)
Environment.Exit
The trick is input from the user always starts out as strings. You must parse those strings into integers. We can do it in very little code like this:
static int sumPrices(IEnumerable<int> prices)
{
return prices.Sum();
}
static void Main(string[] args)
{
int total = sumPrices(args.Select(int.Parse));
Console.WriteLine(total);
Console.ReadKey();
}
See it here (with adjustments for the sandbox environment):
https://dotnetfiddle.net/Pv6B6e
Note this assumes a perfect typist. There is no error checking if a provided value will not parse.
But I doubt using linq operations are expected in an early school project, so we can expand this a little bit to only use techniques that are more familiar:
static int sumPrices(int[] prices)
{
int total = 0;
foreach(int price in prices)
{
total += price;
}
return total;
}
static void Main(string[] args)
{
int[] prices = new int[args.Length];
for(int i = 0; i<args.Length; i++)
{
prices[i] = int.Parse(args[i]);
}
int total = sumPrices(prices);
Console.WriteLine(total);
Console.ReadKey();
}
If you're also not allowed to use int.Parse() (which is nuts, but sometimes homework has odd constraints) you would keep everything else the same, but replace int.Parse() with your own ParseInt() method:
static int ParseInt(string input)
{
int exponent = 0;
int result = 0;
for(int i = input.Length -1; i>=0; i--)
{
result += ((int)(input[i]-'0') * (int)Math.Pow(10, exponent));
exponent++;
}
return result;
}
Again: there's no error checking here for things like out-of-bounds or non-numeric inputs, and this particular implementation only works for positive integers.
I want to have the user input length and width so that we can calculate the area of the rectangle. I have two classes which are Program and Rectangle. The problem is when I try to run this code there is an error:
Program has more than one entry point
How to fix this problem?
Here is my code:
using System;
using System.Collections.Generic;
namespace rec_oop_20211025_E3
{
class Program
{
static void Main(string[] args)
{
List<Rectangle> recs = new List<Rectangle>();
ConsoleColor errColor = ConsoleColor.DarkRed; // to make the error part red in color
ConsoleColor defColor = Console.BackgroundColor; // defColor is default color
start: // use start to run many times
Console.Write("Input sides (eg. 5,6): ");
string line = Console.ReadLine();
Rectangle rec = new Rectangle();
try
{
rec.SetSides(line, ",");
Console.WriteLine($"{rec.GetInfo()}");
recs.Add(rec);
}
catch(Exception ex)
{
Console.BackgroundColor = errColor;
Console.WriteLine($"Error > {ex.Message}");
Console.BackgroundColor = defColor;
}
Console.WriteLine("\nEsc to stop, any key for another rectangle...");
char key = Console.ReadKey(true).KeyChar; // with start we need this
if (key != (char)ConsoleKey.Escape)
goto start;
Console.WriteLine();
Console.BackgroundColor = ConsoleColor.DarkGreen;
double total = 0;
foreach(var r in recs)
{
total += r.GetArea();
Console.WriteLine(r.GetInfo());
}
Console.BackgroundColor = ConsoleColor.DarkBlue;
Console.WriteLine($"\nTotal = {total:n3}");
Console.BackgroundColor = defColor;
}
}
public class Rectangle
{
//static methods
public static void CheckSides(double width, double length)
{
if (width < 0 || length < 0)
throw new Exception($"Rectangle sides( {width}, {length}) are invalid.");
}
public static void TryParse(string data, string delimiter, out double width, out double length)
{
try
{
string[] arr = data.Split(delimiter);
width = double.Parse(arr[0]);
length = double.Parse(arr[1]);
}
catch (Exception)
{
throw new Exception($"Given data \"{data}\" are invalid.");
}
}
//intance fields
private double wd;
private double lng;
//instance methods
public void SetSides(string data, string delimiter)
{
double width, length;
Rectangle.TryParse(data, delimiter, out width, out length);
Rectangle.CheckSides(width, length);
wd = width;
lng = length;
}
public string GetInfo() => $"width={wd}, length={lng} > area={GetArea():n3}";
//public double GetArea() { return wd * lng; }
public double GetArea() => wd * lng;
}
}
}
This is what is this - error is very descriptive. Try search (Ctrl + shift + F) for Main( (just this text main and then 1 round bracket) - you should find another function hidden somewhere that define void Main - this could not be. Only 1 static void Main() could exist. Delete the other one and now you app could compile. Does not matter if you have other classes - the only thing is matter is that function named Main could be only one
P.S. If my answer is not enough - and you still have some question - please make a bare minimum clone of your code and make it Github repo - so we could look at the code
You can check the official documentation on this error:
To resolve this error, you can either delete all Main methods in your code, except one, or you can use the StartupObject compiler option to specify which Main method you want to use.
You can alternatively use the /main switch on the command line to indicate the type containing the appropriate entry point.
I have a wpf c# project and I would like to pass some start up information information in the way I have seen done by other coders only by looking at their short cut
So the short cut I have seen is:
X:\Test.exe /host=[Server Location]/instance=0 /coid=%coid /userid=%oper
I understand the what is being passed, but I would like to understand how the c# project takes the in the information in bold and I guess assigns it in a string etc.
I have tried to google the information but I do not know what to call the topic
Any help - even a no this can not be done would be helpful
See Command Line Parameters Tutorial on MSDN.
An application has an entry point, which is public static void Main(string[] args) in this case. The args parameter contains the command line parameters, split by space.
Edit: my bad, didn't know WPF was obnoxious. Take a look here: WPF: Supporting command line arguments and file extensions:
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args != null && e.Args.Count() > 0)
{
this.Properties["ArbitraryArgName"] = e.Args[0];
}
base.OnStartup(e);
}
You can retrieve command line parameters into string[] with
string[] paramz = Environment.GetCommandLineArgs();
In this example, the program takes one argument at run time, converts the argument to an integer, and calculates the factorial of the number. If no arguments are supplied, the program issues a message that explains the correct usage of the program.
public class Functions
{
public static long Factorial(int n)
{
if (n < 0) { return -1; } //error result - undefined
if (n > 256) { return -2; } //error result - input is too big
if (n == 0) { return 1; }
// Calculate the factorial iteratively rather than recursively:
long tempResult = 1;
for (int i = 1; i <= n; i++)
{
tempResult *= i;
}
return tempResult;
}
}
class MainClass
{
static int Main(string[] args)
{
// Test if input arguments were supplied:
if (args.Length == 0)
{
System.Console.WriteLine("Please enter a numeric argument.");
System.Console.WriteLine("Usage: Factorial <num>");
return 1;
}
try
{
// Convert the input arguments to numbers:
int num = int.Parse(args[0]);
System.Console.WriteLine("The Factorial of {0} is {1}.", num, Functions.Factorial(num));
return 0;
}
catch (System.FormatException)
{
System.Console.WriteLine("Please enter a numeric argument.");
System.Console.WriteLine("Usage: Factorial <num>");
return 1;
}
}
}
Output will be The Factorial of 3 is 6. And the usage of this application is like Factorial.exe <num>
I need to call a program from cmd using an array of numbers(mandatory) and an int time(optional). I have never done this so i'm a bit shaky on the details.
The path is D:\Robert\FactorialConsoleApplication\FactorialConsoleApplication\bin\Debug\FactorialConsoleApplication.exe
As you can tell, the program calculates the factorial of the numbers in the array. The int time is used as a delay in order to display the progress of the stack.
How do I call the program with parameters?
Thanks in advance!
P.S. Here is some of the code
class Program
{
public static void Progress(ProgressEventArgs e)
{
int result = e.getPartialResult;
int stack_value = e.getValue ;
double max = System.Convert.ToDouble(numbers[j]);
System.Convert.ToDouble(stack_value);
double percent = (stack_value / max) * 100;
Console.CursorLeft = 18;
Console.Write(result + " ");
Console.CursorLeft = 46;
Console.Write(System.Convert.ToInt32(percent) + "% ");
}
public static void Calculate(int number, int time=0)
{
Factorial Fact = new Factorial();
Fact.Progression += new Factorial.ProgressEventHandler(Progress);
Console.Write("\n" + "Partial results : ");
Console.CursorLeft = 35;
Console.Write("Progress : ");
int Result = Fact.CalculateFactorial(number, time);
Console.WriteLine(" ");
Console.WriteLine("The factorial of " + number + " is : " + Result);
Console.ReadLine();
}
static int j;
static int[] numbers;
public static void Main(string[] args)
{
int i=0;
bool ok = false;
string line = string.Empty;
numbers = new int[10];
Console.Write("Please insert wait time (0,1 or 2) : ");
int time = int.Parse(Console.ReadLine()) * 1000;
Console.Write("Please insert a number : ");
do
{
line = Console.ReadLine();
if (line != "")
{
i++;
numbers[i] = int.Parse(line);
}
else
{
ok = true;
}
}
while (ok == false);
for (j = 1; j <= i; j++)
{
Calculate(numbers[j],time);
}
}
}
In .net you can use Process.Start from System.Diagnostics to launch an application, you can pass parameters too
For example Process.Start("IExplore.exe", "C:\\myPath\\myFile.htm"); will open Internet Explorer and pass the value "C:\\myPath\\myFile.htm" as parameter to it.For more examplles check the MSDN article on Process.Start method
Update
If in case you are looking to take parameters to your application, when launching itself you don't have to do anything, you are already doing that, the parameter args in your Main method will hold the arguments passed to your application, just try and parse those values in the args array to int array and you are good to go.
Ok, so here is the solution.
I used an args parser like this :
static int extra;
public static void Main(string[] args)
{
foreach (string s in args)
{
extra = int.Parse(s);
Calculate(extra);
}
}
And I changed :double max = System.Convert.ToDouble(numbers[j]);
to :double max = System.Convert.ToDouble(extra);
To call it I open cmd in the directory where the exe is and I type :
Program.exe 3 4 5
It will calculate the factorials of 3, 4 and 5 respectively
I took upon myself to present my team with a situation where a bug would be introduced by the rearrangement of instructions, however my understanding of CPUs, CLR, and JIT is quite amateurish and I did not manage to pull off a good example.
Below I show what is the best I came up with, so please look at the code snippet to understand what I am talking about from here on.
The main point is in thread2's if statement, if it ever happens - it means that the instructions were rearranged. if i manually rearrange the instructions in thread 1 or in thread 2 -> the printing will happened(even if you you swap c.x and c.y reads in thread 2, it will print due to a race condition).
My idea was to force a rearrangement of writes of x and z by making the variables which are placed farther apart integers thinking it could write them both withing one cpu cycle due to the 8 byte word size, instead of it being 3 cycles of writing 4 -> 8 -> 4 bytes. (I know it is not actually 3 cpu cycles, unfortunately, I don't know anything about assembly.) I even tried as a last resort to put it in a struct, thinking that would force some kind of an optimization from JIT.
Any help would be appreciated, because I am very eager to make it work. (I have also tried to follow the examples shown in the ebook by Joseph Albahari, but those did not work, this is why i tried to make a more sophisticated example.) I also did not forget compiling in Release for x64 instruction set.
Code:
public class Program
{
public static void Main()
{
var stopWatch = new Stopwatch();
for (var i = 0; i < 100000000; i++)
{
var delegates = new MultiTreadingDelegates(i);
Task.Run(delegates.Thread1);
Task.Run(delegates.Thread2);
}
Console.WriteLine("finished");
Console.ReadKey();
}
}
public class MultiTreadingDelegates
{
private int i = 0;
private Container container = new Container();
public MultiTreadingDelegates(int i)
{
this.i = i;
}
public void Thread1()
{
container.X = 10000000;
container.Z = 6000000000;
container.Y = 20000000;
}
public void Thread2()
{
int y = container.Y;
long z = container.Z;
int x = container.X;
if (x != 0 && z == 0 && y != 0)
{
System.Console.WriteLine($"i = {i}{Environment.NewLine}"
+ $"x = {x}{Environment.NewLine}"
+ $"z = {z}{Environment.NewLine}"
+ $"y = {y}{Environment.NewLine}"
);
}
}
}
public struct Container
{
public int X;
public long Z;
public int Y;
}
Inspired by the lecture of Sasha Goldshtein - a video i was given as a comment to my question - I have managed to pull off and example of reordering on an Intel machine(code below)! I thank everyone once again for their help.
class Program
{
static void Main(string[] args)
{
Task.Run(DelegatesUsingPetersons.Thread1);
Task.Run(DelegatesUsingPetersons.Thread2).GetAwaiter().GetResult();
}
}
static class DelegatesUsingPetersons
{
private static long x = 0;
private static long y = 0;
private static bool flag1 = false;
private static bool flag2 = false;
public static void Thread1()
{
while (true)
{
flag1 = true;
/*Thread.MemoryBarrier();*/ //Uncomment to fix locking mechanism
if (flag2 == false)
{
x++;
y++;
}
flag1 = false;
}
}
public static void Thread2()
{
long lx = 0;
long ly = 0;
while (true)
{
flag2 = true;
/*Thread.MemoryBarrier();*/ //Uncomment to fix locking mechanism
if (flag1 == false)
{
lx = x;
ly = y;
}
flag2 = false;
if (lx != ly)
{
Console.WriteLine($"lx={lx}, ly={ly} - OMG this cannot happen!");
}
}
}
}
If you want to juxtapose it with a working "more traditional" code, here is the same kind of code just without Mr.Peterson doing all the fancy algorithmic witchcraft
static class DelegatesUsingLock
{
private static long x = 0;
private static long y = 0;
private static object loq = new object();
public static void Thread1()
{
while (true)
{
if (Monitor.TryEnter(loq))
{
x++;
y++;
Monitor.Exit(loq);
}
}
}
public static void Thread2()
{
long lx = 0;
long ly = 0;
while (true)
{
if (Monitor.TryEnter(loq))
{
lx = x;
ly = y;
Monitor.Exit(loq);
}
if (lx != ly)
{
Console.WriteLine($"lx={lx}, ly={ly} - This Never Happens");
}
}
}
}