Онлайн для публичного использования или для передачи кому-то конкретному (другу или кому-то еще)?
Если это с другом, вы можете попробовать перенаправить трафик с вашего маршрутизатора на ваш ПК с определенным портом. Затем создайте простой веб-сайт (например, с Flask, так как он использует python), чтобы сделать игру играбельной. Наконец, другие могут сыграть в игру, зайдя на ваш сайт через ваш общедоступный IP-адрес.
Существует лучший, более абстрактный способ сделать это, которое работает и над WinForms и над WPF:
System.Threading.SynchronizationContext.Current.Post(theMethod, state);
Это работает, потому что WindowsForms устанавливает a WindowsFormsSynchronizationContext
возразите как текущий синхронизирующий контекст. WPF делает что-то подобный, устанавливающий свой собственный специализированный контекст синхронизации (DispatcherSynchronizationContext
).
.Post
соответствует control.BeginInvoke
, и .Send
соответствует control.Invoke
.
Поместите управление UI в метод на форме, которой будут управлять, и передайте делегата в коде, который работает на фоновом потоке, а-ля APM. Вы не должны использовать params object p
, можно сильно ввести его для удовлетворения собственным целям. Это - просто простой универсальный образец.
delegate UiSafeCall(delegate d, params object p);
void SomeUiSafeCall(delegate d, params object p)
{
if (InvokeRequired)
BeginInvoke(d,p);
else
{
//do stuff to UI
}
}
Этот подход утвержден на том, что делегат обращается к методу на конкретном экземпляре; путем создания реализации методом формы Вы приносите форму в объем как this
. Следующее семантически идентично.
delegate UiSafeCall(delegate d, params object p);
void SomeUiSafeCall(delegate d, params object p)
{
if (this.InvokeRequired)
this.BeginInvoke(d,p);
else
{
//do stuff to UI
}
}
Вы правы, не хорошо передать средства управления потокам. Средства управления Winforms являются однопоточными, передавание их к нескольким потокам может вызвать условия состязания или повредить Ваш UI. Вместо этого необходимо сделать функции потока доступными для UI и позволять ему назвать поток, когда UI хорош и готов. Если Вы хотите иметь фоновые потоки, инициировали изменения UI, выставляют фоновое событие и подписываются на него от UI. Поток может исчерпать события каждый раз, когда он хочет, и UI может ответить на них, когда он может.
Создание этой двунаправленной связи между потоками, которая не блокирует поток UI, является большой работой. Вот высоко сокращенный пример с помощью класса BackgroundWorker:
public class MyBackgroundThread : BackgroundWorker
{
public event EventHandler<ClassToPassToUI> IWantTheUIToDoSomething;
public MyStatus TheUIWantsToKnowThis { get { whatever... } }
public void TheUIWantsMeToDoSomething()
{
// Do something...
}
protected override void OnDoWork(DoWorkEventArgs e)
{
// This is called when the thread is started
while (!CancellationPending)
{
// The UI will set IWantTheUIToDoSomething when it is ready to do things.
if ((IWantTheUIToDoSomething != null) && IHaveUIData())
IWantTheUIToDoSomething( this, new ClassToPassToUI(uiData) );
}
}
}
public partial class MyUIClass : Form
{
MyBackgroundThread backgroundThread;
delegate void ChangeUICallback(object sender, ClassToPassToUI uiData);
...
public MyUIClass
{
backgroundThread = new MyBackgroundThread();
// Do this when you're ready for requests from background threads:
backgroundThread.IWantTheUIToDoSomething += new EventHandler<ClassToPassToUI>(SomeoneWantsToChangeTheUI);
// This will run MyBackgroundThread.OnDoWork in a background thread:
backgroundThread.RunWorkerAsync();
}
private void UserClickedAButtonOrSomething(object sender, EventArgs e)
{
// Really this should be done in the background thread,
// it is here as an example of calling a background task from the UI.
if (backgroundThread.TheUIWantsToKnowThis == MyStatus.ThreadIsInAStateToHandleUserRequests)
backgroundThread.TheUIWantsMeToDoSomething();
// The UI can change the UI as well, this will not need marshalling.
SomeoneWantsToChangeTheUI( this, new ClassToPassToUI(localData) );
}
void SomeoneWantsToChangeTheUI(object sender, ClassToPassToUI uiData)
{
if (InvokeRequired)
{
// A background thread wants to change the UI.
if (iAmInAStateWhereTheUICanBeChanged)
{
var callback = new ChangeUICallback(SomeoneWantsToChangeTheUI);
Invoke(callback, new object[] { sender, uiData });
}
}
else
{
// This is on the UI thread, either because it was called from the UI or was marshalled.
ChangeTheUI(uiData)
}
}
}
Что относительно того, чтобы передать Систему. ComponentModel. ISynchronizeInvoke? Тем путем можно постараться не передавать Управление.