PDA

View Full Version : سوال: دسترسی به یک فرم در زمانی که توسط یک async method ساخته شده.



dark-man
سه شنبه 16 اردیبهشت 1399, 20:06 عصر
با سلام خدمت همه اساتید. من یک Socket client در #C با استفاده از async methodهای مربوط به Socket نوشتم. بنا به نیازی که داشتم یک event به اسم OnReceive هم ایجاد کردم که بعد Socket.EndReceive بنا به شرایطی این event اجرا میشه. در کلاسی که OnReceive پیاده سازی شده بنا به شرایطی یک instance از روی یک فرم ایجاد می کنم و نمایش میدم. بعد از نمایش فرم با چند مورد مواجه میشم:

اگر از ()Show استفاده کنم فرم فریز میشه و بعد از چند دقیقه بسته میشه.
اگر از ()ShowDialog استفاده کنم فرم بدون مشکل باز میشه ولی در Main thread نیست.
امکان دسترسی به کنترلهای روی فرم مثلا" یک label یا امکان فراخوانی یک method در فرم رو ندارم.

ممنون میشم راهنماییم کنید با توجه به اینکه بخش مربوط به دریافت داده در Socket رو به صورت async نوشتم و در نهایت یک event اجرا میشه چه طور می تونم به فرمی که ساخته شده دسترسی داشته باشم.

نمونه کد:


// socket.cs
// event
public delegate void ReceiveDataEventHandler(object sender, string data);


public event ReceiveDataEventHandler OnReceiveData;

// call begin receive after connect complete
_masterSocket.BeginReceive(rdm.Data, 0, ReceiveDataModel.DataSize, SocketFlags.None, new AsyncCallback(ReceiveComplete), rdm);

private void ReceiveComplete(IAsyncResult ar)
{
var rdm = (ReceiveDataModel)ar.AsyncState;


var _masterSocket = rdm.Socket;


var receiveLen = _masterSocket.EndReceive(ar);


if (receiveLen > 0)
{
var _rawData = Encoding.UTF8.GetString(rdm.Data, 0, receiveLen);
if (_rawData.EndsWith("EOF"))
{
rdm.DataStorage.Append(_rawData);


var responseData = rdm.DataStorage.ToString();


responseData = responseData.Replace("|EOF", "");


OnReceiveData?.Invoke(this, responseData);


rdm.DataStorage.Clear();


_masterSocket.BeginReceive(rdm.Data, 0, ReceiveDataModel.DataSize, SocketFlags.None, new AsyncCallback(ReceiveComplete), rdm);
}
else
{
rdm.DataStorage.Append(_rawData);
_masterSocket.BeginReceive(rdm.Data, 0, ReceiveDataModel.DataSize, SocketFlags.None, new AsyncCallback(ReceiveComplete), rdm);
}
}
else
{
if (_masterSocket.Connected)
{
_masterSocket.BeginReceive(rdm.Data, 0, ReceiveDataModel.DataSize, SocketFlags.None, new AsyncCallback(ReceiveComplete), rdm);
}
}
}

manager.cs
// constructor
public Manager()
{
new Socket() = msMan;
msMan.OnReceiveData += new Socket.ReceiveDataEventHandler(OnReceiveData);
}

private void OnReceiveData(object sender, string data)
{
var form2 = new Form2();
// form2.Show();
// form2.ShowDialog();
}

ShayanFiroozi
سه شنبه 16 اردیبهشت 1399, 20:20 عصر
سلام دوست گرامی ،

زمان استفاده از Thread , Task , async , BackgroundWorker و امثال اینها اغلب مشکلاتی که در سطح نرم افزار و گاها سیستم عامل اتفاق میفته خطای مهلک CrossThread هستش ، که خیلی وقتا خطا هم صادر نمیشه اما داده ها گم و یا جابجا میشن و یا نرم افزار unstable میشه و crash میکنه.

زمانی که شما از یک ترد و یا شبه ترد مثل Task ها بخواین به اجزای یک ترد دیگه (همه اجزا ، اعم از متغیرها ، توابع ، تعاریف ، اشیاء) دسترسی پیدا کنین صد در صد خطای CrossThread اتفاق میفته.

اما شما با lock کردن اون شی میتونین به شکل ThreadSafe بهش دسترسی پیدا کنین.



private static void SafeSetFormText(Form objForm, string _text)
{


lock (objForm)
{
if (objForm.InvokeRequired)
{
objForm.Invoke(new MethodInvoker(
delegate ()
{
objForm.Text = _text;


}));
}
else
{
objForm.Text = _text;
}
}
}



با این تابع ما میتونیم خصوصیت Text یک فرم رو از یک ترد دیگه تغییر بدیم ( بصورت Safe).

حالا شما میتونین این تابع رو با همین روش برای متد ها و خصوصیات دیگه هم استفاده کنین.

*** ضمنا دسترسی به یک شی در مثلا Form1 نیازمند این هست که شما اون شی رو بصورت public تعریف کنین و در سی شارپ اشیائ در فرم ها بصورت پیش فرض private هستند. که میتونین با تغییر خصوصیت Modifiers اون شی از private به public این کار رو انجام بدین.