我有以下代码
(preproc,calc和display是不同的类)
Thread procThread = new Thread(() => preproc.Start());
procThread.Start();
Thread calcThread = new Thread(() => calc.Start());
calcThread.Start();
Thread displayThread = new Thread(() => display.Start());
displayThread.Start();
在启动方法上preproc.Start()
我有三种方法
lock (data)
{
PreprocessSmth(-10.0, 10.0);
}
//I want to notify next thread here that preprocess ended and now next thread can run
//TODO: data can be calculated now
SaveResultsToDatabase();
DoSomethingElseThatTakesTime();
在PreprocessSmth(-10.0, 10.0);
我想通知calcThread
它现在可以开始之后。
我正在使用,lock
但我不知道它是否可以保证我想要的东西。
编辑:使用TPL我做了类似的事情。那是好习惯吗,还是我搞砸了?
主要方法
Task preprocTask = preproc.StartAsync();
Task calcTask = calc.StartAsync();
Task displayTask = Task.Run(() => display.Start());
Task.WaitAll(preprocTask, calcTask, displayTask);
内部Preproc类StartAsync方法
private Task StartAsync()
{
PreprocessSmth(-10.0, 10.0);
//TODO: data can be calculated now
Task save = SaveResultsToDatabaseAsync();
Task doSomething = DoSomethingElseThatTakesTimeAsync();
return Task.WhenAll(save, doSomething);
}
Calc类StartAsync与Preproc StartAsync几乎相同。所以流程是这样的:Main->preproc.StartAsync()->PreprocessSmth(), then asynchronously SavingToDb and DoSomething, ->calc.StartAsync()->CalcSmth() then SaveDataToDb asynchronously
等等...
OP:
如何从C#中的其他类通知线程...以及PreprocessSmth(-10.0,10.0)之后;我想通知calcThread它可以立即开始。
因此,首先进行审查。如果我们检查你的原始代码:
Thread procThread = new Thread(() => preproc.Start());
procThread.Start();
Thread calcThread = new Thread(() => calc.Start());
calcThread.Start();
Thread displayThread = new Thread(() => display.Start());
displayThread.Start();
......我们可以看到,三(3)Thread
创建s到开车preproc
,calc
并display
和所有处于运行状态。我了解你想通知calcThread
它可能PreprocessSmth
在完成后就开始了。
与老式Thread
编程紧密结合的一种简单方法是Windows同步对象,它是大多数操作系统的通用功能。
MSDN对此有此看法(我的重点是):
甲同步对象是一个对象,其手柄可以在等待函数之一指定来协调多个线程的执行。多个进程可以具有同一个同步对象的句柄,从而使进程间同步成为可能。告诉我更多...
...和NET(我的重点):
.NET提供了一系列类型,你可以使用这些类型来同步对共享资源的访问或协调线程交互...从
System.Threading.WaitHandle
该类派生出多个.NET同步原语,该类封装了本机操作系统同步句柄并为线程使用了信令机制相互作用。告诉我更多...
...具体来说:
System.Threading.ManualResetEvent
从中获得EventWaitHandle
并在发出信号时保持在信号状态,直到Reset
调用该方法为止。 告诉我更多...
想到的最简单的方式ManualResetEvent
就像浴室的水龙头。最初可以将其关闭,但是如果将其打开,水龙头将一直保持这种状态,直到你家中的某人将其关闭为止……或者当当地市议会削减你的供水以支付未付账单时。
考虑到这一点,让我们ManualResetEvent
像这样向你的代码介绍:
class Program
{
#region Statics
static void Main(string[] args)
{
Logger.WriteLine($"Inside Main, thread ID is {Environment.CurrentManagedThreadId}");
// initial state is NOT set (i.e. the "bathroom tap" is off)
var goodToGoEvent = new ManualResetEvent(false); // <--- NEW
var preproc = new PreProc();
var procThread = new Thread(() => preproc.Start(goodToGoEvent)); // pass in event
procThread.Start();
var calc = new Calc();
var calcThread = new Thread(() => calc.Start(goodToGoEvent)); // pass in event
calcThread.Start();
var display = new Display();
var displayThread = new Thread(() => display.Start());
displayThread.Start();
// wait for Calc to finish
Logger.WriteLine("Main thread - waiting for Calc thread to complete...");
calcThread.Join();
Logger.WriteLine("Main thread - waiting for Calc thread to complete...OK");
Logger.WriteLine("Press any key to exit");
Console.ReadKey();
}
#endregion
}
internal class Display
{
#region Methods
public void Start()
{
// NOP
}
#endregion
}
internal class Calc
{
#region Methods
/// <summary>
/// Starts the specified good to go event.
/// </summary>
/// <param name="goodToGoEvent">The event to wait on that indicates "good-to-go".</param>
public void Start(ManualResetEvent goodToGoEvent)
{
Logger.WriteLine("Calc - waiting for good-to-go event to be signaled...");
goodToGoEvent.WaitOne(); // block current thread until event is signaled
Logger.WriteLine("Calc - waiting for good-to-go event to be signaled...OK");
// now that PreProc is complete do what needs to be done here
}
#endregion
}
internal class PreProc
{
#region Fields
private object data = new object();
#endregion
#region Methods
/// <summary>
/// Starts the specified good to go event.
/// </summary>
/// <param name="goodToGoEvent">the event to signal once processing is complete.</param>
public void Start(ManualResetEvent goodToGoEvent)
{
//lock (data) // <--- not necessary in this example unless other threads will modify data concurrently
{
PreprocessSmth(-10.0, 10.0);
}
//I want to notify next thread here that preprocess ended and now next thread can run
Logger.WriteLine("PreProc - setting goodToGoEvent to signaled");
goodToGoEvent.Set(); // let other threads know that preprocess has ended
//TODO: data can be calculated now
SaveResultsToDatabase();
DoSomethingElseThatTakesTime();
}
private void DoSomethingElseThatTakesTime()
{
// NOP
}
private void PreprocessSmth(double d, double d1)
{
Logger.WriteLine("PreProc - Preprocessing...(sleeping 5 secs)");
Thread.Sleep(TimeSpan.FromSeconds(5)); // simulate lengthy operation
Logger.WriteLine("PreProc - Preprocessing...OK");
}
private void SaveResultsToDatabase()
{
// NOP
}
#endregion
}
public static class Logger
{
#region Static fields
private static readonly object Locker = new object();
#endregion
#region Statics
public static void WriteLine(string message)
{
lock (Locker)
{
Console.WriteLine($"{DateTime.Now:hh:mm:ss.ff} [{Environment.CurrentManagedThreadId}] {message}");
}
}
#endregion
}
在我的代码中,我导出了一个新goodToGoEvent
的类型变量,ManualResetEvent
该变量同时传递给preproc.Start
和calc.Start
。这是工作preproc
到Set
而事件calc
Wait
S代表它。可以将其视为一组交通信号灯。
这是运行中的代码,方括号([ ])中显示的数字是相应的线程ID:
根据你使用/受限于的.NET版本,你可能要查看Microsoft的Task Parallel Library,Task
以及如何将它们全部打包在功能区中async/await
(最好是.NET 4.5+经验)。
如果我这么大胆:
无论工作是否受I / O约束,async / await是进行异步编程的最现代,最简便的方法。任务是否需要线程。免费自我推广
非常感谢您的解释和代码示例!您的回答明确了我们可以将ManualResetEvent传递给两个对象。请问,如果
display.Start()
我有类似的情况怎么办,所以其中的一种方法display
必须等待中的一种方法calc
。我创建了新事件displayEvent
,并将其传递给calc
和display
。这是正确的方法吗?没问题。是的,如果这
display
取决于calc
您的操作方式。