C# 如何在主线程中读取/写入全局变量
我创建了一个C#Windows IoT后台应用程序。该应用程序的线程池中有多个线程无限期运行 这些线程需要能够读/写主线程中的全局变量,但我不确定如何实现这一点。下面是我正在尝试做的一个例子:C# 如何在主线程中读取/写入全局变量,c#,multithreading,iot,windows-10-iot-core,C#,Multithreading,Iot,Windows 10 Iot Core,我创建了一个C#Windows IoT后台应用程序。该应用程序的线程池中有多个线程无限期运行 这些线程需要能够读/写主线程中的全局变量,但我不确定如何实现这一点。下面是我正在尝试做的一个例子: // main task public sealed class StartupTask : IBackgroundTask { private static BackgroundTaskDeferral _Deferral = null; private static MyThread
// main task
public sealed class StartupTask : IBackgroundTask
{
private static BackgroundTaskDeferral _Deferral = null;
private static MyThreadClass1 thread1 = null;
private static MyThreadClass2 thread2 = null;
private static MyThreadClass3 thread3 = null;
List<Object> MyDevices = null;
public async void Run(IBackgroundTaskInstance taskInstance)
{
_Deferral = taskInstance.GetDeferral();
MyDevices = GetDeviceList();
thread1 = new MyThreadClass1();
await ThreadPool.RunAsync(workItem =>
{
thread1.Start();
});
thread2 = new MyThreadClass2();
await ThreadPool.RunAsync(workItem =>
{
thread2.Start();
});
thread3 = new MyThreadClass3();
await ThreadPool.RunAsync(workItem =>
{
thread3.Start();
});
}
}
internal class MyThreadClass1
{
public async void Start()
{ }
}
internal class MyThreadClass2
{
public async void Start()
{ }
}
internal class MyThreadClass3
{
public async void Start()
{ }
}
//主要任务
公共密封类StartupTask:IBackgroundTask
{
私有静态背景任务延迟_deleral=null;
私有静态MyThreadClass1 thread1=null;
私有静态MyThreadClass2 thread2=null;
私有静态MyThreadClass3 thread3=null;
List MyDevices=null;
公共异步无效运行(IBackgroundTaskInstance taskInstance)
{
_延迟=taskInstance.getDeleral();
MyDevices=GetDeviceList();
thread1=新的MyThreadClass1();
等待ThreadPool.RunAsync(workItem=>
{
thread1.Start();
});
thread2=新的MyThreadClass2();
等待ThreadPool.RunAsync(workItem=>
{
thread2.Start();
});
thread3=新的MyThreadClass3();
等待ThreadPool.RunAsync(workItem=>
{
thread3.Start();
});
}
}
内部类MyThreadClass1
{
公共异步void Start()
{ }
}
内部类MyThreadClass2
{
公共异步void Start()
{ }
}
内部类MyThreadClass3
{
公共异步void Start()
{ }
}
在正在运行的三个线程中的任何一个线程中,我都需要能够读写列出我的设备
这些线程都有不同的功能,但它们都与“MyDevices”交互,因此如果一个线程对该列表进行了更改,其他线程需要立即了解更改
做这件事最好的方法是什么
谢谢 >你想考虑的一件事是A.此类实现了
INotifyPropertyChanged
接口,该接口通知任何侦听器底层集合的更改
接下来,您需要在线程
类中为属性更改
实现一个事件处理程序,如下所示(我建议创建一个接口或基类来处理此问题,因为您似乎对每个线程
使用不同的类):
公共密封类MyThreadBase
{
专用可观测采集设备;
公共MyThreadBase(ObservableCollection deviceList)
{
我的设备=设备主义者;
MyDevices.PropertyChanged+=MyDevices\u PropertyChanged;//注册侦听器
}
私有void MyDevices\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
锁(我的设备)
{
//对数据做点什么。。。
}
}
}
使用lock
语句,以便在另一个线程正在读取或写入MyDevices
时阻止该线程。这在同步中通常很重要,称为。我建议大家仔细研究一下这个问题以及可能的解决办法
但是,如果您打算让每个线程在设备上迭代并对每个设备执行某些操作,那么您将遇到问题,因为迭代更改的集合不是一个好主意(并且在使用foreach
循环时,实际上会抛出一个异常),因此也请记住这一点
其他线程需要立即了解更改
如果您想要低延迟通知,线程必须将大部分时间花在睡眠上。例如,执行Dispatcher.Run()
,它将休眠等待消息/任务处理
如果是这种情况,您可以使用ObservableCollection
而不是List,并编写CollectionChanged
处理程序来转发3个线程的通知。或者,如果您不希望启动更改以处理更改事件的线程,则可以将通知转发给其他两个线程(当前线程除外)
我不确定Dispatcher
类是否在Windows IoT平台上可用。绝对不是.NETCore的情况。即使没有,也可以使用高级构建块来创建一个。这里的示例实现也实现了同步上下文,非常简单,因为它依赖于高级ConcurrentQueue和BlockingCollection泛型类
using kvp = KeyValuePair<SendOrPostCallback, object>;
enum eShutdownReason : byte
{
Completed,
Failed,
Unexpected,
}
class Dispatcher : IDisposable
{
const int maxQueueLength = 100;
readonly ConcurrentQueue<kvp> m_queue;
readonly BlockingCollection<kvp> m_block;
public Dispatcher()
{
m_queue = new ConcurrentQueue<kvp>();
m_block = new BlockingCollection<kvp>( m_queue, maxQueueLength );
createdThreadId = Thread.CurrentThread.ManagedThreadId;
prevContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext( new SyncContext( this ) );
}
readonly SynchronizationContext prevContext;
readonly int createdThreadId;
class SyncContext : SynchronizationContext
{
readonly Dispatcher dispatcher;
public SyncContext( Dispatcher dispatcher )
{
this.dispatcher = dispatcher;
}
// https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
public override void Post( SendOrPostCallback cb, object state )
{
dispatcher.Post( cb, state );
}
}
/// <summary>Run the dispatcher. Must be called on the same thread that constructed the object.</summary>
public eShutdownReason Run()
{
Debug.Assert( Thread.CurrentThread.ManagedThreadId == createdThreadId );
while( true )
{
kvp h;
try
{
h = m_block.Take();
}
catch( Exception ex )
{
ex.logError( "Dispatcher crashed" );
return eShutdownReason.Unexpected;
}
if( null == h.Key )
return (eShutdownReason)h.Value;
try
{
h.Key( h.Value );
}
catch( Exception ex )
{
ex.logError( "Exception in Dispatcher.Run" );
}
}
}
/// <summary>Signal dispatcher to shut down. Can be called from any thread.</summary>
public void Stop( eShutdownReason why )
{
Logger.Info( "Shutting down, because {0}", why );
Post( null, why );
}
/// <summary>Post a callback to the queue. Can be called from any thread.</summary>
public void Post( SendOrPostCallback cb, object state = null )
{
if( !m_block.TryAdd( new kvp( cb, state ) ) )
throw new ApplicationException( "Unable to post a callback to the dispatcher: the dispatcher queue is full" );
}
void IDisposable.Dispose()
{
Debug.Assert( Thread.CurrentThread.ManagedThreadId == createdThreadId );
SynchronizationContext.SetSynchronizationContext( prevContext );
}
}
使用kvp=KeyValuePair;
枚举eShutdownReason:字节
{
完整的,
失败,
想不到的
}
类调度程序:IDisposable
{
const int maxQueueLength=100;
只读并发队列m_队列;
只读块集合m_块;
公共调度程序()
{
m_queue=新的ConcurrentQueue();
m_block=新BlockingCollection(m_队列,maxQueueLength);
createdThreadId=Thread.CurrentThread.ManagedThreadId;
prevContext=SynchronizationContext.Current;
SetSynchronizationContext(新的SyncContext(this));
}
只读同步上下文prevContext;
只读int createdThreadId;
类SyncContext:SynchronizationContext
{
只读调度器;
公共同步上下文(调度程序)
{
this.dispatcher=dispatcher;
}
// https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
公共覆盖无效Post(SendOrPostCallback cb,对象状态)
{
调度员岗(cb,州);
}
}
///必须在构造该对象的同一线程上调用。
公共eShutdownReason运行()
{
Assert(Thread.CurrentThread.ManagedThreadId==createdThreadId);
while(true)
{
kvph;
尝试
{
h=m_块。取();
}
捕获(例外情况除外)
{
例如logError(“
using kvp = KeyValuePair<SendOrPostCallback, object>;
enum eShutdownReason : byte
{
Completed,
Failed,
Unexpected,
}
class Dispatcher : IDisposable
{
const int maxQueueLength = 100;
readonly ConcurrentQueue<kvp> m_queue;
readonly BlockingCollection<kvp> m_block;
public Dispatcher()
{
m_queue = new ConcurrentQueue<kvp>();
m_block = new BlockingCollection<kvp>( m_queue, maxQueueLength );
createdThreadId = Thread.CurrentThread.ManagedThreadId;
prevContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext( new SyncContext( this ) );
}
readonly SynchronizationContext prevContext;
readonly int createdThreadId;
class SyncContext : SynchronizationContext
{
readonly Dispatcher dispatcher;
public SyncContext( Dispatcher dispatcher )
{
this.dispatcher = dispatcher;
}
// https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
public override void Post( SendOrPostCallback cb, object state )
{
dispatcher.Post( cb, state );
}
}
/// <summary>Run the dispatcher. Must be called on the same thread that constructed the object.</summary>
public eShutdownReason Run()
{
Debug.Assert( Thread.CurrentThread.ManagedThreadId == createdThreadId );
while( true )
{
kvp h;
try
{
h = m_block.Take();
}
catch( Exception ex )
{
ex.logError( "Dispatcher crashed" );
return eShutdownReason.Unexpected;
}
if( null == h.Key )
return (eShutdownReason)h.Value;
try
{
h.Key( h.Value );
}
catch( Exception ex )
{
ex.logError( "Exception in Dispatcher.Run" );
}
}
}
/// <summary>Signal dispatcher to shut down. Can be called from any thread.</summary>
public void Stop( eShutdownReason why )
{
Logger.Info( "Shutting down, because {0}", why );
Post( null, why );
}
/// <summary>Post a callback to the queue. Can be called from any thread.</summary>
public void Post( SendOrPostCallback cb, object state = null )
{
if( !m_block.TryAdd( new kvp( cb, state ) ) )
throw new ApplicationException( "Unable to post a callback to the dispatcher: the dispatcher queue is full" );
}
void IDisposable.Dispose()
{
Debug.Assert( Thread.CurrentThread.ManagedThreadId == createdThreadId );
SynchronizationContext.SetSynchronizationContext( prevContext );
}
}
object _mutex = new object();
List<Object> MyDevices = null;
...
var device = ...;
lock (_mutex)
{
MyDevices.Add(device);
}