gpt4 book ai didi

c# - 如何在正在运行的线程上调用方法?

转载 作者:行者123 更新时间:2023-12-01 09:55:30 24 4
gpt4 key购买 nike

在控制台应用程序上,我目前正在启动一个线程数组。线程被传递一个对象并在其中运行一个方法。我想知道如何在各个运行线程内的对象上调用方法。

调度员不工作。 SynchronizationContext "Send"在调用线程上运行,而 "Post"使用一个新线程。我希望能够调用该方法并在它运行的目标线程上的运行线程上传递参数,而不是调用线程。

更新 2:示例代码

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CallingFromAnotherThread
{
class Program
{
static void Main(string[] args)
{
var threadCount = 10;
var threads = new Thread[threadCount];
Console.WriteLine("Main on Thread " + Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < threadCount; i++)
{
Dog d = new Dog();
threads[i] = new Thread(d.Run);
threads[i].Start();
}
Thread.Sleep(5000);

//how can i call dog.Bark("woof");
//on the individual dogs and make sure they run on the thread they were created on.
//not on the calling thread and not on a new thread.
}

}

class Dog
{
public void Run()
{
Console.WriteLine("Running on Thread " + Thread.CurrentThread.ManagedThreadId);
}

public void Bark(string text)
{
Console.WriteLine(text);
Console.WriteLine("Barking on Thread " + Thread.CurrentThread.ManagedThreadId);
}
}
}

更新 1:
使用同步上下文.将结果发送到使用调用线程
Channel created
Main thread 10
SyncData Added for thread 11
Consuming channel ran on thread 11
Calling AddConsumer on thread 10
Consumer added consumercb78b. Executed on thread 10
Calling AddConsumer on thread 10
Consumer added consumer783c4. Executed on thread 10

使用同步上下文.Post 结果使用不同的线程
Channel created
Main thread 10
SyncData Added for thread 11
Consuming channel ran on thread 11
Calling AddConsumer on thread 12
Consumer added consumercb78b. Executed on thread 6
Calling AddConsumer on thread 10
Consumer added consumer783c4. Executed on thread 7

最佳答案

目标线程必须“在自身上”运行代码——或者它只是跨线程访问对象。这是通过目标线程本身上的某种形式的事件调度循环来完成的。

如果底层提供者支持,SynchronizationContext 抽象可以并且确实支持它。例如在 WinForms 或 WPF(它们本身使用“窗口消息泵”)中使用 Post将“在 UI 线程上运行”。

基本上,所有这些构造都遵循模式的一些变化:

// On "target thread"
while (running) {
var action = getNextDelegateFromQueue();
action();
}

// On other thread
postDelegateToQueue(actionToDoOnTargetThread);

手动创建原始队列系统相当简单——只需确保使用正确的同步守卫。 (尽管我确信那里有整洁的“已解决问题”库;包括将所有内容包装到 SynchronizationContext 中。)

这是手动队列的原始版本。请注意,可能存在竞争条件……但是,FWIW:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace DogPark
{
internal class DogPark
{

private readonly string _parkName;
private readonly Thread _thread;
private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
private volatile bool _isOpen;

public DogPark(string parkName)
{
_parkName = parkName;
_isOpen = true;
_thread = new Thread(OpenPark);
_thread.Name = parkName;
_thread.Start();
}

// Runs in "target" thread
private void OpenPark(object obj)
{
while (true)
{
Action action;
if (_actions.TryDequeue(out action))
{
Program.WriteLine("Something is happening at {0}!", _parkName);
try
{
action();
}
catch (Exception ex)
{
Program.WriteLine("Bad dog did {0}!", ex.Message);
}
}
else
{
// Nothing left!
if (!_isOpen && _actions.IsEmpty)
{
return;
}
}

Thread.Sleep(0); // Don't toaster CPU
}
}

// Called from external thread
public void DoItInThePark(Action action)
{
if (_isOpen)
{
_actions.Enqueue(action);
}
}

// Called from external thread
public void ClosePark()
{
_isOpen = false;
Program.WriteLine("{0} is closing for the day!", _parkName);
// Block until queue empty.
while (!_actions.IsEmpty)
{
Program.WriteLine("Waiting for the dogs to finish at {0}, {1} actions left!", _parkName, _actions.Count);

Thread.Sleep(0); // Don't toaster CPU
}
Program.WriteLine("{0} is closed!", _parkName);
}

}

internal class Dog
{

private readonly string _name;

public Dog(string name)
{
_name = name;
}

public void Run()
{
Program.WriteLine("{0} is running at {1}!", _name, Thread.CurrentThread.Name);
}

public void Bark()
{
Program.WriteLine("{0} is barking at {1}!", _name, Thread.CurrentThread.Name);
}

}

internal class Program
{
// "Thread Safe WriteLine"
public static void WriteLine(params string[] arguments)
{
lock (Console.Out)
{
Console.Out.WriteLine(arguments);
}
}

private static void Main(string[] args)
{
Thread.CurrentThread.Name = "Home";

var yorkshire = new DogPark("Yorkshire");
var thunderpass = new DogPark("Thunder Pass");

var bill = new Dog("Bill the Terrier");
var rosemary = new Dog("Rosie");

bill.Run();

yorkshire.DoItInThePark(rosemary.Run);
yorkshire.DoItInThePark(rosemary.Bark);

thunderpass.DoItInThePark(bill.Bark);

yorkshire.DoItInThePark(rosemary.Run);

thunderpass.ClosePark();
yorkshire.ClosePark();
}

}
}

输出应如下所示 - 请记住,由于非同步线程的固有性质,多次运行时这会发生变化。

Bill the Terrier is running at Home!
Something is happening at Thunder Pass!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Bill the Terrier is barking at Thunder Pass!
Something is happening at Yorkshire!
Rosie is barking at Yorkshire!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Thunder Pass is closing for the day!
Thunder Pass is closed!
Yorkshire is closing for the day!
Yorkshire is closed!

没有什么能阻止一只狗同时在多个狗公园表演。

1 存在比赛条件,它是这样的:公园可能在最后一次狗行动开始之前关闭。

这是因为 dog park 线程在 action 运行之前使 action 出队——而关闭 dog park 的方法只会等到所有的 action 出队。

有多种解决方法,例如:
  • 并发队列可以先 peek-use-then-dequeue-after-the-action,或者
  • 可以使用单独的 volatile isClosed-for-real 标志(从狗公园线程设置),或者 ..

  • 我已经把这个错误留在了提醒线程的危险中..

    关于c# - 如何在正在运行的线程上调用方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28772886/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com