The Thread class has 4 different constructores:
Thread(ParameterizedThreadStart)
Thread(ThreadStart)
Thread(ParameterizedThreadStart, Int32)
Thread(ThreadStart, Int32)
But why this following code works?
class Program {
static void Main(string[] args) {
Thread t = new Thread(count2);
t.Start();
count1();
Console.ReadLine();
}
public static void count1() {
for (int i = 0; i < 10; i++) {
Console.WriteLine("count1: " + i);
}
}
public static void count2() {
for (int i = 0; i < 10; i++) {
Console.WriteLine("count2: " + i);
}
}
}
I don't pass the Thread constructore a ParameterizedThreadStart-delegate or a ThreadStart-delegate. I pass only a normal count2 method. But why this works?
That is because the compiler automatically adds a delegate construction for you, and it infers the type of delegate.
This page documents the exact feature.
Related
Let's say I have a multithreading console application:
class Program
{
private static void Method()
{
Console.WriteLine("Method() started");
for (var i = 0; i < 20; i++)
{
Console.WriteLine($"Counter = {i}");
Thread.Sleep(500);
}
Console.WriteLine("Method() finished");
}
static void Main(string[] args)
{
Console.WriteLine("Main Started");
var task = new Task(Method);
task.Start();
for (var i = 0; i < 60; i++)
{
Console.Write(".");
Thread.Sleep(100);
}
Console.WriteLine("Main finished");
Console.ReadLine();
}
}
My question is how does compiler knows which delegate to use inside ..= new Task(..)?
I mean how compiler replace this stroke
var task = new Task(Method);
with this
var action = new Action(Method);
var task = new Task(action);
The constructor for the Action accepts a void method
new Action(someVoidMethod);
The compiler actually generated this code:
Task task = new Task(new Action(Program.Method));
Since you pass a void method with no parameters, the compiler can convert it into Action.
/*My requirements is
one threads should print even numbers and the other should print odd numbers.
These threads should print the numbers in order (1, 2, 3, 4, 5...)
I have done this code but when my commenting either method countThreadOdd.Start() or countThreadEven.Start(), it is not printing only even or odd numbers.*/
class Program
{
static Object locker = new Object();
static LinkedList<int> number = new LinkedList<int>();
static int counter = 0;
static void Main(string[] args)
{
Thread countThreadOdd = new Thread(oddThread);
Thread countThreadEven = new Thread(evenThread);
//Thread Start
countThreadOdd.Start();
countThreadEven.Start();
//main thread will untill below thread is in exection mode
countThreadOdd.Join(10);
countThreadEven.Join(10);
Console.ReadLine();
}
//Odd Thread
public static void oddThread()
{
for (; counter < 10; )
{
//Lock the another thread to enter in critial area
lock (locker)
{
if (counter % 2 != 0)
{
Console.WriteLine(counter);
counter++;
}
}
}
}
//Even Thread
public static void evenThread()
{
for (; counter < 10; )
{
//Lock the another thread to enter in critial area
lock (locker)
{
if (counter % 2 == 0)
{
Console.WriteLine(counter);
counter++;
}
}
}
}
}
If you want to alternate between two threads, you can use two AutoResetEvent objects to do so, like so:
public static void oddThread()
{
for (int i = 1; i < 10; i +=2)
{
evenReady.WaitOne();
Console.WriteLine(i);
oddReady.Set();
}
}
public static void evenThread()
{
for (int i = 0; i < 10; i += 2)
{
oddReady.WaitOne();
Console.WriteLine(i);
evenReady.Set();
}
}
If you only want to run one of the threads, you can use a ManualResetEvent instead to effectively remove all locking.
A full example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Dmr.Common.Resources;
namespace Demo
{
class Program
{
static EventWaitHandle evenReady;
static EventWaitHandle oddReady;
static void Main(string[] args)
{
bool countOdd = true; // Change these to true/false as wanted.
bool countEven = true;
if (countOdd && countEven)
{
evenReady = new AutoResetEvent(false);
oddReady = new AutoResetEvent(true); // Must be true for the starting thread.
}
else
{
evenReady = new ManualResetEvent(true);
oddReady = new ManualResetEvent(true);
}
Thread countThreadOdd = new Thread(oddThread);
Thread countThreadEven = new Thread(evenThread);
//Thread Start
if (countOdd)
countThreadOdd.Start();
if (countEven)
countThreadEven.Start();
//main thread will untill below thread is in exection mode
if (countOdd)
countThreadOdd.Join();
if (countEven)
countThreadEven.Join();
Console.WriteLine("Done");
Console.ReadLine();
}
public static void oddThread()
{
for (int i = 1; i < 10; i +=2)
{
evenReady.WaitOne();
Console.WriteLine(i);
oddReady.Set();
}
}
public static void evenThread()
{
for (int i = 0; i < 10; i += 2)
{
oddReady.WaitOne();
Console.WriteLine(i);
evenReady.Set();
}
}
}
}
static AutoResetEvent evenReady = new AutoResetEvent(true);
static AutoResetEvent oddReady = new AutoResetEvent(false);
static void Main()
{
Thread countThreadOdd = new Thread(oddThread);
Thread countThreadEven = new Thread(evenThread);
countThreadOdd.Start();
countThreadEven.Start();
Console.WriteLine("Done");
Console.ReadLine();
}
public static void oddThread()
{
for (int i = 1; i < 10; i += 2)
{
oddReady.Set();
evenReady.WaitOne();
Console.WriteLine("Odd Thread: " + i);
//oddReady.Set();
}
}
public static void evenThread()
{
for (int i = 0; i < 10; i += 2)
{
oddReady.WaitOne();
evenReady.Set();
Console.WriteLine("Even Thread: " + i);
}
}
You can actually use Interlocked to communicate between threads. Interlocked allows you to share a variable concurrently across two threads.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace InterlockedTest
{
class Program
{
private static long _counter = 0;
private static void printEvenTask()
{
while (Interlocked.Read(ref _counter) < 100)
{
if (Interlocked.Read(ref _counter) % 2 == 0)
{
Console.WriteLine(Interlocked.Read(ref _counter));
Interlocked.Increment(ref _counter);
}
}
}
private static void printOddTask()
{
while (Interlocked.Read(ref _counter) < 100)
{
if (Interlocked.Read(ref _counter) % 2 == 1)
{
Console.WriteLine(Interlocked.Read(ref _counter));
Interlocked.Increment(ref _counter);
}
}
}
static void Main(string[] args)
{
Task oddTask = Task.Run(() => printOddTask());
Task evenTask = Task.Run(() => printEvenTask());
oddTask.Wait();
evenTask.Wait();
Console.ReadKey();
}
}
}
Try this method. It uses Task Library.
public class OddEvenThread
{
public static async Task printEvenNumber(int n)
{
for (int i = 1; i <= n; i++)
{
if (i % 2 == 0)
Console.WriteLine(i);
}
await Task.Delay(0);
}
private static async Task printOddNumbers(int n)
{
for (int i = 1; i <= n; i++)
{
if (i % 2 == 1)
Console.WriteLine(i);
}
await Task.Delay(0);
}
public async static Task printNumbers(int n)
{
Task evenNumbers = printEvenNumber(n);
Task oddNumbers = printOddNumbers(n);
List<Task> tasks = new List<Task>() { evenNumbers, oddNumbers };
await Task.WhenAll(tasks);
}
}
Using AutoResetEvent, threads can be made wait for each other. Here, two threads write numbers from 1 to 20:
using System;
using System.Threading;
namespace oddeven
{
class Program
{
static void Main(string[] args)
{
C c = new C();
Thread t1 = new Thread(c.PrintOdd);
Thread t2 = new Thread(c.PrintEven);
t1.Start();
t2.Start();
}
}
class C
{
AutoResetEvent e1 = new AutoResetEvent(true);
AutoResetEvent e2 = new AutoResetEvent(true);
int j = 1;
public void PrintOdd()
{
while (j < 20)
{
if (j % 2 != 0)
{
Console.WriteLine(j);
j++;
}
e1.Set();
e2.WaitOne();
}
}
public void PrintEven()
{
while (j <= 20)
{
e1.WaitOne();
if (j % 2 == 0)
{
Console.WriteLine(j);
j++;
}
e2.Set();
}
}
}
}
i have app with gui
I put function checkproxy() in Form1.cs it works correctly and i want move function checkproxy() to other class but if i put checkproxy() in other class it will error with Invoke and richTextBox3
namespace test3
{
public partial class Form1 : Form
{
public bool continueThreads = false;
string[] proxyList = null;
List<Thread> threadList = new List<Thread>();
int proxynum = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int n = (int)numericUpDown1.Value;
Thread[] tl = new Thread[n + 1];
threadList = tl.ToList();
for (int i = 0; i <= n; i++)
{
threadList[i] = new Thread(new ThreadStart(checkproxy));
}
for (int i = 0; i <= n; i++)
{
threadList[i].Start();
}
continueThreads = true;
proxyList = richTextBox1.Lines;
}
public void checkproxy()
{
while (continueThreads)
{
if (proxynum >= proxyList.Length)
{
continueThreads = false;
}
if (proxynum < proxyList.Length)
{
string proxy = proxyList[proxynum];
proxynum += 1;
string info = "";
try
{
Thread.Sleep(1000);
info += "Live || " + proxy + Environment.NewLine;
this.Invoke(new Action(() => richTextBox3.Text += info));
}
catch
{
}
}
}
}
}
}
this is screenshot error
Your method checkproxy uses Form1 class members (continueThreads, proxynum and others) directly.
If you really want do move it outside of this class (I'm not sure it is good idea since this method looks very closely related to your class) - you need to refactor this method and pass all class members it uses as method input parameters like
public void checkproxy(bool continueThreads.....)
Because this is a System.Windows.Forms.Form in original context.
To be able to Invoke interface update from another thread/async task, you need to use it (as you did correctly in your original code).
But once you move the function into separate class, there is no more notion of a Conntrol or Form there, so this is a class itself, which does not have Invoke implementation.
One possible solution: you need to refactor your method in a way, that he is able to call Form's function, that internally calls Invoke.
I'm creating a class library with a method, for example, OnetoTen(), which basically is a for loop counting from 1 to 10. What i'm trying to achieve is to call this method from another program and have it output what number/iteration the for loop is currently at.
Is the use of delegates/events the right way to go?
You could use a callback (delegate) or an event.
Example using callback:
class Program
{
static void Main(string[] args)
{
var counter = new Counter();
counter.CountUsingCallback(WriteProgress);
Console.ReadKey();
}
private static void WriteProgress(int progress, int total){
Console.WriteLine("Progress {0}/{1}", progress, total);
}
}
public class Counter
{
public void CountUsingCallback(Action<int, int> callback)
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
callback(i + 1, 10);
}
}
}
Example using event:
class Program
{
static void Main(string[] args)
{
var counter = new Counter();
counter.ProgessTick += WriteProgress;
counter.CountUsingEvent();
Console.ReadKey();
}
private static void WriteProgress(int progress, int total){
Console.WriteLine("Progress {0}/{1}", progress, total);
}
}
public class Counter
{
public event Action<int, int> ProgessTick;
public void CountUsingEvent()
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
if (ProgessTick != null)
ProgessTick(i + 1, 10);
}
}
}
I can send one parameter in the thread
class myThread
{
Thread thread;
public myThread(string name, int st)
{
thread = new Thread(this.Get_IP);
thread.Name = name;
thread.Start(st);//передача параметра в поток
thread.Join();
}
void Get_IP(object st)
{
for (int ii = 0; ii < (int)st; ii++)
{
// smth
}
}
}
But i need to send two of them
for example
for (int ii = (int)st; ii < (int)fi; ii++)
{
// smth
}
there is a way to put 2 params all together
void A(int a, int b) { }
and
ThreadStart starter = delegate { A(0, 10); };
But how can i send them to the thread?
You can pass more parameters to thread by using lambda expresion. Like this:
Thread thread = new Thread(()=>A(5,6));
Put the two variables as members in the class:
class MyThread {
private Thread _thread;
private int _start, _finish;
public MyThread(string name, int start, int finish) {
_start = start;
_finish = finish;
_thread = new Thread(Get_IP);
_thread.Name = name;
_thread.Start();
_thread.Join();
}
void Get_IP() {
for (int ii = _start; ii < _finish; ii++) {
// smth
}
}
}
Note: Calling Join right after starting the thread makes is pretty pointless to use a thread.
The Thread.Start method accepts an object as parameter. You can pass an array of your values.
thread.Start(new object[] { a, b });
Create a class that hold all of the values you need to pass and pass an instance of that class to your thread.
If you need to send 2 parametes, you can send them as any type you like, but in the method that starts new thread, you have to unbox it/them:
void MyMethod()
{
int a = 1;
int b = 2;
int[] data = new[] { a, b };
Thread t = new Thread(new ParameterizedThreadStart(StartThread));
t.Start(data);
}
private void StartThread(object obj)
{
int[] data = obj as int[];
if (data != null)
{
int a = data[0];
int b = data[1];
}
}
NOTE: method that is called by new Thread can only accppt object parameter. What is inside this object is not code`s concern, can be anything, like I boxes 2 integers.
Then you simply unbox the object to your original data types.