වෙනත් නූල් එකකින් GUI යාවත්කාලීන කරන්නේ කෙසේද?


1405

එය යාවත්කාලීන කිරීම සඳහා පහසුම මඟ කුමක් ද Labelතවත් සිට Thread?

  • මට Formධාවනය වෙමින් පවතින අතර thread1, එයින් මම තවත් ත්‍රෙඩ් එකක් ආරම්භ කරමි thread2.

  • අතර thread2සමහර ගොනු සකස් කරනවා මම යාවත්කාලීන කිරීමට කැමති Labelමත Formවත්මන් තත්ත්වය සමග thread2ගේ වැඩ.

මම එය කරන්නේ කෙසේද?


25
.Net 2.0+ ට මේ සඳහා පසුබිම් වැඩ පන්තිය නොමැත. එය UI නූල් දැනුවත් ය. 1. පසුබිම් සේවකයෙකු සාදන්න 2. නියෝජිතයින් දෙදෙනෙකු එක් කරන්න (එක් අයෙකු පිරිසැකසුම් කිරීම සඳහා සහ එක් අයෙකු සම්පුර්ණ කිරීම සඳහා)
ප්‍රීත් සං ha

13
ටිකක් ප්‍රමාද විය හැක: codeproject.com/KB/cs/Threadsafe_formupdating.aspx
මයිකල් ඩී

4
.NET 4.5 සහ C # 5.0 සඳහා පිළිතුර බලන්න: stackoverflow.com/a/18033198/2042090
Ryszard Dżegan

5
මෙම ප්‍රශ්නය Gtk # GUI සඳහා අදාළ නොවේ. Gtk # සඳහා මෙය සහ මෙම පිළිතුර බලන්න.
hlovdal

පරෙස්සම් වන්න: මෙම ප්‍රශ්නයට පිළිතුරු දැන් OT ("මෙන්න මගේ WPF යෙදුම සඳහා මම කළ දේ") සහ .තිහාසික .NET 2.0 කෞතුක වස්තු අවුල් සහගත අවුල් ජාලයක්.
මාක් එල්

Answers:


773

.NET 2.0 සඳහා, මෙන්න මම ලියූ ලස්සන කේතයක් ඔබට අවශ්‍ය දේ හරියටම කරන අතර ඕනෑම දේපලකට වැඩ කරන්නේ Control:

private delegate void SetControlPropertyThreadSafeDelegate(
    Control control, 
    string propertyName, 
    object propertyValue);

public static void SetControlPropertyThreadSafe(
    Control control, 
    string propertyName, 
    object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
        propertyName, 
        BindingFlags.SetProperty, 
        null, 
        control, 
        new object[] { propertyValue });
  }
}

එය මේ ආකාරයට අමතන්න:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

ඔබ .NET 3.0 හෝ ඊට වැඩි භාවිතා කරන්නේ නම්, ඔබට ඉහත ක්‍රමය Controlපන්තියේ ව්‍යාප්ති ක්‍රමයක් ලෙස නැවත ලිවිය හැකිය , එමඟින් ඇමතුම සරල කරයි:

myLabel.SetPropertyThreadSafe("Text", status);

යාවත්කාලීන කිරීම 05/10/2010:

.NET 3.0 සඳහා ඔබ මෙම කේතය භාවිතා කළ යුතුය:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
    Control @this, 
    Expression<Func<TResult>> property, 
    TResult value);

public static void SetPropertyThreadSafe<TResult>(
    this Control @this, 
    Expression<Func<TResult>> property, 
    TResult value)
{
  var propertyInfo = (property.Body as MemberExpression).Member 
      as PropertyInfo;

  if (propertyInfo == null ||
      !@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
          propertyInfo.Name, 
          propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
      @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }
}

එය වඩා පිරිසිදු, සරල හා ආරක්ෂිත සින්ටැක්ස් වලට ඉඩ දීම සඳහා ලින්ක් සහ ලැම්බඩා ප්‍රකාශන භාවිතා කරයි:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

සංයුක්ත වේලාවේදී දේපල නාමය දැන් පරීක්ෂා කර ඇතිවා පමණක් නොව, දේපල වර්ගය ද වේ, එබැවින් (නිදසුනක් ලෙස) බූලියන් දේපලකට නූල් අගයක් පැවරිය නොහැක, එබැවින් ධාවන කාල ව්‍යතිරේකයක් ඇති කරයි.

අවාසනාවකට මෙය වෙනත් කෙනෙකුගේ Controlදේපළ හා වටිනාකම පසු කිරීම වැනි මෝඩ දේවල් කිරීමෙන් කිසිවෙකු වළක්වන්නේ නැත , එබැවින් පහත සඳහන් දෑ සතුටින් සම්පාදනය කරනු ඇත:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

එනිසා සම්මත කළ දේපල ඇත්ත වශයෙන්ම Controlක්‍රමයට කැඳවනු ලබන ක්‍රමයට අයත් බව සහතික කිරීම සඳහා මම ධාවන කාල චෙක්පත් එක් කළෙමි . පරිපූර්ණ නොවේ, නමුත් .NET 2.0 අනුවාදයට වඩා බොහෝ හොඳය.

සම්පාදක කාල ආරක්ෂාව සඳහා මෙම කේතය වැඩි දියුණු කරන්නේ කෙසේද යන්න පිළිබඳව යමෙකුට තවත් යෝජනා තිබේ නම්, කරුණාකර අදහස් දක්වන්න!


3
GetType () දේපලInfo.ReflectedType (උදා: WinForms හි LinkLabel) ට සමාන ලෙස තක්සේරු කරන අවස්ථා තිබේ. මට විශාල C # අත්දැකීමක් නැත, නමුත් මම සිතන්නේ ව්‍යතිරේකය සඳහා කොන්දේසිය විය යුත්තේ: if (propertyInfo == null || (!@this.GetType (). IsSubclassOf (propertyInfo.ReflectedType) && @ this.GetType ( )! = propertyInfo.ReflectedType) || @ this.GetType (). GetProperty (propertyInfo.Name, propertyInfo.PropertyType) == null)
Corvin

9
@lan මෙය SetControlPropertyThreadSafe(myLabel, "Text", status)වෙනත් මොඩියුලයකින් හෝ පන්තියකින් හෝ ආකෘතියකින් හැඳින්විය හැකිය
ස්මිත්

71
සපයන විසඳුම අනවශ්‍ය ලෙස සංකීර්ණයි. ඔබ සරල බව අගය කරන්නේ නම් මාක් ග්‍රෙවෙල්ගේ විසඳුම හෝ සයිඩ් මසුඩ්ගේ විසඳුම බලන්න.
ෆ්‍රෑන්ක් හිල්මන්

8
සෑම ආයාචනයක් සඳහාම සම්පත් විශාල ප්‍රමාණයක් වැය වන බැවින් ඔබ බහු දේපල යාවත්කාලීන කරන්නේ නම් මෙම විසඳුම ටොන් සම්පත් නාස්ති කරයි. කෙසේ වෙතත් Thread Safety හි විශේෂාංගය අදහස් කළේ කෙසේදැයි මම නොසිතමි. ඔබගේ UI යාවත්කාලීන ක්‍රියාවන් එන්කැප්සල්ට් කර එය එක් වරක් ආයාචනා කරන්න (සහ එක් දේපලකට නොවේ)
කොන්සෝලය

4
BackgroundWorker සංරචකය හරහා ඔබ මෙම කේතය භාවිතා කරන්නේ ඇයි?
ඇන්ඩි

1090

මෙම සරල ක්රමය සම්මත නිර්නාමික ක්රමය වේ Label.Invoke:

// Running on the worker thread
string newText = "abc";
form.Label.Invoke((MethodInvoker)delegate {
    // Running on the UI thread
    form.Label.Text = newText;
});
// Back on the worker thread

Invokeඑය සම්පූර්ණ වන තුරු ක්‍රියාත්මක කිරීම අවහිර කරන බව සලකන්න - මෙය සමමුහුර්ත කේතයයි. අසමමුහුර්ත කේතය ගැන ප්‍රශ්නය අසන්නේ නැත, නමුත් ඔබට ඒ ගැන ඉගෙන ගැනීමට අවශ්‍ය වූ විට අසමමුහුර්ත කේත ලිවීම ගැන ස්ටැක් පිටාර ගැලීම් වල අන්තර්ගතයන් රාශියක් ඇත.


8
ආකෘති පත්‍රය හැර වෙනත් කිසිදු පංතියක් / අවස්ථාවක් OP සඳහන් කර නැති නිසා එය නරක පෙරනිමියක් නොවේ ...
මාක් ග්‍රෙවෙල්

39
"මෙම" මූල පදය "පාලන" පන්තියක් යොමු කිරීම අමතක නොකරන්න.
AZ.

8
ode කේත සම්පුර්ණ කිරීම දෙයාකාරයකින්ම ආරක්ෂිත වන අතර, අපි සේවකයෙකු මත සිටින බව අපි දැනටමත් දනිමු, එබැවින් අප දන්නා දෙයක් පරීක්ෂා කරන්නේ ඇයි?
මාක් ග්‍රෙවෙල්

4
Ra ඩ්‍රැගුෆ් ඇත්ත වශයෙන්ම නොවේ - මෙම ක්‍රමය භාවිතා කිරීමේ එක් කරුණක් නම්, සේවකයා මත ධාවනය වන කොටස් සහ UI නූල් මත ධාවනය වන කොටස් ඔබ දැනටමත් දන්නා බැවිනි. පරීක්ෂා කිරීමට අවශ්‍ය නැත.
මාක් ග්‍රෙවෙල්

3
@ Joan.bdm මට ඒ පිළිබඳව අදහස් දැක්වීමට තරම් සන්දර්භයක් නොමැත
මාක් ග්‍රෙවෙල්

406

දිගු වැඩ කිරීම

සිට .NET 4.5 හා C # 5.0 ඔබ භාවිතා කළ යුත්තේ කාර්ය මත පදනම් Asynchronous රටාව (TAP) සමග අසමකාලීක - ඉදිරියේදී බලාපොරොත්තු වන්න ප්රධාන වචන සියලු ප්රදේශවල (හට GUI ඇතුළුව):

TAP යනු නව සංවර්ධනය සඳහා නිර්දේශිත අසමමුහුර්ත මෝස්තර රටාවයි

වෙනුවට Asynchronous ආදර්ශ (APM) ක්රමලේඛ හා Event මත පදනම් Asynchronous රටාව (EAP) (මෙම දෙවැන්න ද ඇතුළත් BackgroundWorker පන්තියේ ).

නව සංවර්ධනය සඳහා නිර්දේශිත විසඳුම වන්නේ:

  1. සිදුවීම් හසුරුවන්නෙකුගේ අසමමුහුර්තව ක්‍රියාත්මක කිරීම (ඔව්, එපමණයි):

    private async void Button_Clicked(object sender, EventArgs e)
    {
        var progress = new Progress<string>(s => label.Text = s);
        await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress),
                                    TaskCreationOptions.LongRunning);
        label.Text = "completed";
    }
  2. UI නූල් දැනුම් දෙන දෙවන නූල් ක්‍රියාත්මක කිරීම:

    class SecondThreadConcern
    {
        public static void LongWork(IProgress<string> progress)
        {
            // Perform a long running work...
            for (var i = 0; i < 10; i++)
            {
                Task.Delay(500).Wait();
                progress.Report(i.ToString());
            }
        }
    }

පහත සඳහන් කරුණු සැලකිල්ලට ගන්න:

  1. කෙටි හා පිරිසිදු කේත අනුපිළිවෙලින් ඇමතුම් සහ පැහැදිලි නූල් නොමැතිව ලියා ඇත.
  2. නූල් වෙනුවට කාර්යය .
  3. අසින්ක් යතුරුපදය, එමඟින් බලා‍පොරොත්තු වීමට ඉඩ සලසයි, එමඟින් කාර්යය අවසන් වන තුරු සිදුවීම් හසුරුවන්නා සම්පූර්ණ තත්වයට පත්වීම වලක්වනු ඇති අතර මේ අතර UI නූල් අවහිර නොකරයි.
  4. ප්‍රගති පන්තිය ( IProgress අතුරුමුහුණත බලන්න ) එය සම්බන්ධතා වෙන් කිරීමේ (SoC) සැලසුම් මූලධර්මයට සහාය වන අතර පැහැදිලි පිටත්කර යැවීමක් සහ ආයාචනයක් අවශ්‍ය නොවේ. එය නිර්මාණය කරන ස්ථානයේ සිට වත්මන් සමමුහුර්තකරණය භාවිතා කරයි (මෙහි UI නූල්).
  5. TaskCreationOptions.LongRunning මඟින් එම කාර්යය ThreadPool වෙත පෝලිම් ගැසීමට ඉඟි නොකරයි .

වඩාත් වාචික උදාහරණ සඳහා බලන්න: සී # හි අනාගතය: ජෝසප් අල්බහාරි විසින් 'බලා සිටින' අයට යහපත් දේ පැමිණේ .

UI Threading Model සංකල්පය ගැනද බලන්න .

ව්යතිරේක හැසිරවීම

Enabledපසුබිම් ක්‍රියාත්මක කිරීමේදී බහු ක්ලික් කිරීම් වලක්වා ගැනීම සඳහා ව්‍යතිරේකයන් හැසිරවිය යුතු ආකාරය සහ බොත්තම් දේපල ටොගල් කිරීම සඳහා පහත ස්නිපටය නිදසුනකි .

private async void Button_Click(object sender, EventArgs e)
{
    button.Enabled = false;

    try
    {
        var progress = new Progress<string>(s => button.Text = s);
        await Task.Run(() => SecondThreadConcern.FailingWork(progress));
        button.Text = "Completed";
    }
    catch(Exception exception)
    {
        button.Text = "Failed: " + exception.Message;
    }

    button.Enabled = true;
}

class SecondThreadConcern
{
    public static void FailingWork(IProgress<string> progress)
    {
        progress.Report("I will fail in...");
        Task.Delay(500).Wait();

        for (var i = 0; i < 3; i++)
        {
            progress.Report((3 - i).ToString());
            Task.Delay(500).Wait();
        }

        throw new Exception("Oops...");
    }
}

2
SecondThreadConcern.LongWork()ව්‍යතිරේකයක් විසි කරන්නේ නම් , එය UI නූල් මගින් අල්ලා ගත හැකිද? මෙය විශිෂ්ට තනතුරකි, btw.
kdbanman

2
ඔබගේ අවශ්‍යතා සපුරාලීම සඳහා මම පිළිතුරට අමතර කොටසක් එකතු කර ඇත්තෙමි. සුභ පැතුම්.
රයිස්සාඩ් ඩීගන්

3
මෙම ExceptionDispatchInfo පන්ති අසමකාලීක-ඉදිරියේදී බලාපොරොත්තු වන්න රටාව තුළ UI නූල් පසුබිම පිළිබඳ හැර rethrowing බව ආශ්චර්යය සඳහා වගකිව යුතු වේ.
රයිස්සාඩ් ඩීගන්

1
ආයාචනා කිරීම / ආරම්භ කිරීම ආයාචනා කරනවාට වඩා මෙය සිදු කරන ආකාරය වාචික යැයි සිතීම මා පමණක්ද?!
MeTitus

2
Task.Delay(500).Wait()? වත්මන් නූල් අවහිර කිරීම සඳහා කාර්යයක් නිර්මාණය කිරීමේ තේරුම කුමක්ද? ඔබ කිසි විටෙකත් නූල් තටාක නූල් අවහිර නොකළ යුතුය!
යරික්

243

.NET 4 සඳහා මාක් ග්‍රෙවෙල්ගේ සරලම විසඳුමේ විචලනය :

control.Invoke((MethodInvoker) (() => control.Text = "new text"));

හෝ ඒ වෙනුවට ක්‍රියාකාරී නියෝජිතයා භාවිතා කරන්න:

control.Invoke(new Action(() => control.Text = "new text"));

මේ දෙක සංසන්දනය කිරීම සඳහා මෙහි බලන්න: MethodInvoker vs Action for Control.BeginInvoke


1
මෙම උදාහරණයේ 'පාලනය' යනු කුමක්ද? මගේ UI පාලනය? ලේබල් පාලනයක් මත WPF හි මෙය ක්‍රියාත්මක කිරීමට උත්සාහ කරන අතර ඉන්වොක් මගේ ලේබලයේ සාමාජිකයෙක් නොවේ.
ඩබ්ලූම්

@Styxriver stackoverflow.com/a/3588137/206730 වැනි දිගු කිරීමේ ක්‍රමය ගැන කුමක් කිව හැකිද?
කික්නෙට්

'ක්‍රියාව y;' පංතියේ හෝ ක්‍රමයේ ඇතුළත පෙළ දේපල වෙනස් කර 'yourcontrol.Invoke (y = () => yourcontrol.Text = "new text");
ඇන්ටෝනියෝ ලයිට්

4
B බ්ලූම් එය සාමාජිකයෙකු නොවේ, මන්ද එය වින් ෆෝම්ස් සඳහා පමණි. WPF සඳහා ඔබ භාවිතා කරන්නේ Dispatcher.Invoke
sLw

1
මම මෙම විසඳුම අනුගමනය කළ නමුත් සමහර විට මගේ UI යාවත්කාලීන නොවීය. this.refresh()GUI අවලංගු කර නැවත පින්තාරු කිරීමට මට අවශ්‍ය බව මට පෙනී ගියේය .. එය ප්‍රයෝජනවත් නම් ..
රකිබුල් හක්

139

.NET 3.5+ සඳහා ගිනි හා අමතක කිරීමේ දිගු කිරීමේ ක්‍රමය

using System;
using System.Windows.Forms;

public static class ControlExtensions
{
    /// <summary>
    /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
    /// </summary>
    /// <param name="control"></param>
    /// <param name="code"></param>
    public static void UIThread(this Control @this, Action code)
    {
        if (@this.InvokeRequired)
        {
            @this.BeginInvoke(code);
        }
        else
        {
            code.Invoke();
        }
    }
}

පහත දැක්වෙන කේත රේඛාව භාවිතයෙන් මෙය හැඳින්විය හැකිය:

this.UIThread(() => this.myLabel.Text = "Text Goes Here");

5
මෙම භාවිතයේ තේරුම කුමක්ද? "පාලනය" සමාන නොවේද? මෙයින් යම් ප්‍රතිලාභ තිබේද?
argyle

14
ejeromeyers - @thisමෙය හුදෙක් විචල්ය නාමයයි, මේ අවස්ථාවේ දී වත්මන් පාලනයට යොමු කිරීම දිගුව ලෙස හැඳින්වේ. ඔබට එය ප්‍රභවයක් ලෙස නම් කළ හැකිය, නැතහොත් ඔබේ බෝට්ටුව පාවෙන ඕනෑම දෙයක්. මම භාවිතා කරන්නේ @this, එය 'මෙම පාලනය' ගැන සඳහන් කරන අතර එය දිගුව ලෙස හඳුන්වන අතර (මගේ හිසෙහි අවම වශයෙන්) සාමාන්‍ය (දිගුව නොවන) කේතයේ 'මෙම' මූල පදය භාවිතා කිරීම සමඟ ස්ථාවර වේ.
ස්ටයික්ස් රිවර්

1
මෙය විශිෂ්ටයි, පහසුයි, මට හොඳම විසඳුමයි. ඔබට කළ යුතු සියලු කාර්යයන් ui ත්‍රෙඩ් එකට ඇතුළත් කළ හැකිය. උදාහරණය: this.UIThread (() => {txtMessage.Text = message; listBox1.Items.Add (message);});
ඔටෝ

1
මම ඇත්තටම මෙම විසඳුමට කැමතියි. සුළු නයිට්: මම මෙම ක්‍රමයට OnUIThreadවඩා නම් තබමි UIThread.
මෙවලම් සාදන්නා ස්ටීව්

2
ඒ නිසයි මම මෙම දිගුව නම් කළේ RunOnUiThread. නමුත් එය පෞද්ගලික රසය පමණි.
ග්‍රිස්ග්‍රෑම්

68

ඔබ මෙය කළ යුතු සම්භාව්‍ය ක්‍රමය මෙයයි:

using System;
using System.Windows.Forms;
using System.Threading;

namespace Test
{
    public partial class UIThread : Form
    {
        Worker worker;

        Thread workerThread;

        public UIThread()
        {
            InitializeComponent();

            worker = new Worker();
            worker.ProgressChanged += new EventHandler<ProgressChangedArgs>(OnWorkerProgressChanged);
            workerThread = new Thread(new ThreadStart(worker.StartWork));
            workerThread.Start();
        }

        private void OnWorkerProgressChanged(object sender, ProgressChangedArgs e)
        {
            // Cross thread - so you don't get the cross-threading exception
            if (this.InvokeRequired)
            {
                this.BeginInvoke((MethodInvoker)delegate
                {
                    OnWorkerProgressChanged(sender, e);
                });
                return;
            }

            // Change control
            this.label1.Text = e.Progress;
        }
    }

    public class Worker
    {
        public event EventHandler<ProgressChangedArgs> ProgressChanged;

        protected void OnProgressChanged(ProgressChangedArgs e)
        {
            if(ProgressChanged!=null)
            {
                ProgressChanged(this,e);
            }
        }

        public void StartWork()
        {
            Thread.Sleep(100);
            OnProgressChanged(new ProgressChangedArgs("Progress Changed"));
            Thread.Sleep(100);
        }
    }


    public class ProgressChangedArgs : EventArgs
    {
        public string Progress {get;private set;}
        public ProgressChangedArgs(string progress)
        {
            Progress = progress;
        }
    }
}

ඔබේ සේවක නූලට සිදුවීමක් තිබේ. ඔබගේ UI නූල් වැඩ කිරීම සඳහා තවත් ත්‍රෙඩ් එකකින් ආරම්භ කර එම සේවක සිදුවීම සම්බන්ධ කර ගන්නා අතර එවිට ඔබට සේවක නූල්වල තත්වය පෙන්විය හැකිය.

එවිට UI හි සත්‍ය පාලනය වෙනස් කිරීම සඳහා නූල් තරණය කළ යුතුය ... ලේබලයක් හෝ ප්‍රගති තීරුවක් වැනි.


64

සරල විසඳුම භාවිතා කිරීමයි Control.Invoke.

void DoSomething()
{
    if (InvokeRequired) {
        Invoke(new MethodInvoker(updateGUI));
    } else {
        // Do Something
        updateGUI();
    }
}

void updateGUI() {
    // update gui here
}

සරල බව සඳහා හොඳින් කර ඇත! සරල පමණක් නොව, හොඳින් ක්රියා කරයි! මයික්‍රොසොෆ්ට් එය සරල කිරීමට නොහැකි වූයේ මන්දැයි මට තේරුණේ නැත. ප්‍රධාන නූලට 1 පේළියක් ඇමතීම සඳහා, අපි කාර්යයන් කිහිපයක් ලිවිය යුතුය!
MBH

1
BMBH එකඟයි. BTW, දිගු කිරීමේ ක්‍රමයක් නිර්වචනය කරන stackoverflow.com/a/3588137/199364 පිළිතුර ඔබ දුටුවාද ? වරක් අභිරුචි උපයෝගිතා පන්තියක එය කරන්න, ඉන්පසු මයික්‍රොසොෆ්ට් අප වෙනුවෙන් එය නොකළ බවට තවදුරටත් සැලකිලිමත් විය යුතු නැත :)
මෙවලම් සාදන්නා ස්ටීව්

@ToolmakerSteve එය හරියටම අදහස් කළේ එයයි! ඔබ හරි, අපට ක්‍රමයක් සොයාගත හැකිය, නමුත් මම අදහස් කළේ ඩ්‍රයි (ඔබම පුනරාවර්තනය නොකරන්න) දෘෂ්ටි කෝණයෙන්, පොදු විසඳුමක් ඇති ගැටළුව, මයික්‍රොසොෆ්ට් හි අවම උත්සාහයෙන් ඔවුන් විසින් විසඳා ගත හැකි අතර එමඟින් බොහෝ කාලයක් ඉතිරි වේ ක්‍රමලේඛකයින් :)
MBH

49

නූල් කේතය බොහෝ විට දෝෂ සහිත වන අතර පරීක්ෂා කිරීමට සෑම විටම අපහසුය. පසුබිම් කාර්යයකින් පරිශීලක අතුරුමුහුණත යාවත්කාලීන කිරීම සඳහා ඔබට නූල් කේත ලිවීමට අවශ්‍ය නොවේ. කාර්යය ක්‍රියාත්මක කිරීම සඳහා BackgroundWorker පන්තිය සහ පරිශීලක අතුරුමුහුණත යාවත්කාලීන කිරීම සඳහා එහි වාර්තා ප්‍රගති ක්‍රමය භාවිතා කරන්න. සාමාන්‍යයෙන්, ඔබ වාර්තා කරන්නේ ප්‍රතිශතයක් සම්පූර්ණයි, නමුත් රාජ්‍ය වස්තුවක් ඇතුළත් තවත් අධි බරක් තිබේ. මෙන්න නූල් වස්තුවක් වාර්තා කරන උදාහරණයක්:

    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "A");
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "B");
        Thread.Sleep(5000);
        backgroundWorker1.ReportProgress(0, "C");
    }

    private void backgroundWorker1_ProgressChanged(
        object sender, 
        ProgressChangedEventArgs e)
    {
        label1.Text = e.UserState.ToString();
    }

ඔබට සෑම විටම එකම ක්ෂේත්‍රය යාවත්කාලීන කිරීමට අවශ්‍ය නම් එය හොඳයි. ඔබට වඩාත් සංකීර්ණ යාවත්කාලීන කිරීම් තිබේ නම්, ඔබට UI තත්වය නියෝජනය කිරීම සඳහා පන්තියක් නිර්වචනය කර එය වාර්තා ප්‍රගති ක්‍රමයට යොමු කළ හැකිය.

එක් අවසාන දෙයක් නම්, WorkerReportsProgressධජය සැකසීමට වග බලා ගන්න , නැතහොත් ReportProgressක්‍රමය සම්පූර්ණයෙන්ම නොසලකා හරිනු ඇත.


2
පිරිසැකසුම් කිරීම අවසානයේදී, පරිශීලක අතුරුමුහුණත හරහා යාවත්කාලීන කළ හැකිය backgroundWorker1_RunWorkerCompleted.
ඩේවිඩ් ආර්ආර්

44

උත්තරයන්ගෙන් බහුතරයක් භාවිතා Control.Invokeකිරීමට බලාපොරොත්තු වන ධාවන තත්වයකි . උදාහරණයක් ලෙස, පිළිගත් පිළිතුර සලකා බලන්න:

string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate { 
    someLabel.Text = newText; // runs on UI thread
});

පරිශීලක ආකෘති පත්රය පමණක් පෙර වසා දමයි නම්, this.Invoke(මතක තබා, ලෙස හැඳින්වේ thisයනු Formවස්තුව), යනු ObjectDisposedExceptionඉඩ වෙඩි වනු ඇත.

විසඳුම භාවිතා කිරීමයි SynchronizationContext, විශේෂයෙන් SynchronizationContext.Currentලෙස hamilton.danielb යෝජනා (වෙනත් පිළිතුරු විශේෂිත මත රඳා SynchronizationContextඅනවශ්ය වන නිර්මාණයන්). මම ඔහුගේ කේතය භාවිතා SynchronizationContext.Postකිරීමට වඩා තරමක් වෙනස් කරමි SynchronizationContext.Send(සාමාන්‍යයෙන් සේවක නූල් රැඳී සිටීමට අවශ්‍ය නොවන බැවින්):

public partial class MyForm : Form
{
    private readonly SynchronizationContext _context;
    public MyForm()
    {
        _context = SynchronizationContext.Current
        ...
    }

    private MethodOnOtherThread()
    {
         ...
         _context.Post(status => someLabel.Text = newText,null);
    }
}

.NET 4.0 සහ ඊට ඉහළින් ඔබ ඇත්ත වශයෙන්ම අසංක මෙහෙයුම් සඳහා කාර්යයන් භාවිතා කළ යුතු බව සලකන්න. සමාන කාර්ය පාදක ප්‍රවේශය සඳහා (භාවිතා කිරීම ) n-san ගේ පිළිතුර බලන්න TaskScheduler.FromCurrentSynchronizationContext.

අවසාන වශයෙන්, .NET 4.5 සහ ඊට ඉහළින් ඔබට භාවිතා කළ හැකිය Progress<T>(එය මූලික වශයෙන් SynchronizationContext.Currentඑය නිර්මාණය කිරීම ග්‍රහණය කරගනී ) රයිස්සාඩ් ඩීගන් විසින් නිරූපණය කර ඇති පරිදි දිගු කාලීනව ක්‍රියාත්මක වන විට UI කේතය ක්‍රියාත්මක කිරීමට අවශ්‍ය අවස්ථාවන්හිදී.


39

යාවත්කාලීනය නිවැරදි නූල් මත සිදු වන බවට ඔබට සහතික විය යුතුය; UI නූල්.

මෙය සිදු කිරීම සඳහා, ඔබට එය සෘජුවම ඇමතීම වෙනුවට සිදුවීම් හසුරුවන්නාට ආයාචනා කළ යුතුය.

ඔබේ සිදුවීම මේ ආකාරයට මතු කිරීමෙන් ඔබට මෙය කළ හැකිය:

(කේතය මෙහි මගේ හිසෙන් ටයිප් කර ඇත, එබැවින් මම නිවැරදි සින්ටැක්ස් ආදිය පරීක්ෂා කර නැත, නමුත් එය ඔබට යා යුතුය.)

if( MyEvent != null )
{
   Delegate[] eventHandlers = MyEvent.GetInvocationList();

   foreach( Delegate d in eventHandlers )
   {
      // Check whether the target of the delegate implements 
      // ISynchronizeInvoke (Winforms controls do), and see
      // if a context-switch is required.
      ISynchronizeInvoke target = d.Target as ISynchronizeInvoke;

      if( target != null && target.InvokeRequired )
      {
         target.Invoke (d, ... );
      }
      else
      {
          d.DynamicInvoke ( ... );
      }
   }
}

WPF පාලනයන් ISynchronizeInvokeඅතුරු මුහුණත ක්‍රියාත්මක නොකරන බැවින් ඉහත කේතය WPF ව්‍යාපෘති මත ක්‍රියා නොකරන බව සලකන්න .

වග බලා ගන්න ඉහත වින්ඩෝස් ආකෘති පත්ර සහ WPF, හා අනෙකුත් සියළුම වේදිකා සමග ක්රියා කේතය බවට වගබලා ගැනීම සඳහා, ඔබ, මේ බලන්න පුළුවන් AsyncOperation, AsyncOperationManagerසහ SynchronizationContextපන්ති.

මේ ආකාරයෙන් සිදුවීම් පහසුවෙන් මතු කිරීම සඳහා, මම දිගු කිරීමේ ක්‍රමයක් නිර්මාණය කර ඇති අතර, එමඟින් ඇමතුමක් ලබා දීමෙන් සිදුවීමක් මතු කිරීම සරල කිරීමට මට ඉඩ සලසයි:

MyEvent.Raise(this, EventArgs.Empty);

ඇත්ත වශයෙන්ම, ඔබට BackGroundWorker පන්තියද භාවිතා කළ හැකිය, එය ඔබට මෙම කාරණය සාරාංශ කරයි.


ඇත්ත වශයෙන්ම, නමුත් මම මේ කාරණය සමඟ මගේ GUI කේතය 'අවුල් කිරීමට' කැමති නැත. මගේ GUI එය ආයාචනා කළ යුතුද නැද්ද යන්න ගණන් නොගත යුතුය. වෙනත් වචන වලින් කිවහොත්: සන්දර්භය වෙනස් කිරීම GUI හි වගකීම යැයි මම නොසිතමි.
ෆෙඩ්රික් ගයිසල්ස්

1
නියෝජිතයා වෙන් කිරීම යනාදිය අතිරික්තයක් සේ පෙනේ - ඇයි පමණක් නොවේ: සමමුහුර්තකරණය කොන්ටෙක්ස්ට්
මාක් ග්‍රෙවෙල්

ඔබට සැමවිටම සමමුහුර්තකරණයට ප්‍රවේශය තිබේද? ඔබේ පන්තිය පංතියේ ලිබ් එකක වුවද?
ෆෙඩ්රික් ගයිසල්ස්

31

ඔබට GUI නූල් මත ක්‍රමය ක්‍රියාත්මක කිරීමට අවශ්‍ය වනු ඇත. Control.Invoke ඇමතීමෙන් ඔබට එය කළ හැකිය.

උදාහරණයක් වශයෙන්:

delegate void UpdateLabelDelegate (string message);

void UpdateLabel (string message)
{
    if (InvokeRequired)
    {
         Invoke (new UpdateLabelDelegate (UpdateLabel), message);
         return;
    }

    MyLabelControl.Text = message;
}

1
ඉන්වොක් රේඛාව මට සම්පාදක දෝෂයක් ලබා දෙයි. 'System.Windows.Forms.Control.Invoke (System.Delegate, object [])' සඳහා හොඳම බර පැටවූ ක්‍රම ගැලපීම වලංගු නොවන තර්ක කිහිපයක් ඇත
CruelIO

30

සිද්ධියේ සුළුකම නිසා මට ඇත්ත වශයෙන්ම තත්වය සඳහා UI නූල් මත විමසුම ලැබෙනු ඇත. එය තරමක් අලංකාර විය හැකි බව ඔබ සොයා ගනු ඇතැයි මම සිතමි.

public class MyForm : Form
{
  private volatile string m_Text = "";
  private System.Timers.Timer m_Timer;

  private MyForm()
  {
    m_Timer = new System.Timers.Timer();
    m_Timer.SynchronizingObject = this;
    m_Timer.Interval = 1000;
    m_Timer.Elapsed += (s, a) => { MyProgressLabel.Text = m_Text; };
    m_Timer.Start();
    var thread = new Thread(WorkerThread);
    thread.Start();
  }

  private void WorkerThread()
  {
    while (...)
    {
      // Periodically publish progress information.
      m_Text = "Still working...";
    }
  }
}

ප්රවේශය ISynchronizeInvoke.Invokeසහ ISynchronizeInvoke.BeginInvokeක්රම භාවිතා කරන විට අවශ්ය මාර්ෂල් කිරීමේ ක්රියාකාරිත්වය වළක්වයි . මාෂල් කිරීමේ තාක්ෂණය භාවිතා කිරීමේ කිසිදු වරදක් නැත, නමුත් ඔබ දැනුවත් විය යුතු අවවාද කිහිපයක් තිබේ.

  • ඔබ BeginInvokeනිතර කතා නොකරන බවට වග බලා ගන්න හෝ එය පණිවිඩ පොම්පය අභිබවා යා හැකිය.
  • ඇමතුම් Invoke, කම්කරුවාගේ නූල් මත අවහිර ඇමතුමක් වේ. එම ත්‍රෙඩ් එකේ සිදු කෙරෙන වැඩ තාවකාලිකව නවතා දමනු ඇත.

මෙම පිළිතුරේ මා යෝජනා කරන උපායමාර්ගය නූල්වල සන්නිවේදන භූමිකාවන් ආපසු හරවයි. සේවක නූල් වෙනුවට දත්ත තල්ලු කිරීම වෙනුවට UI නූල් ඡන්ද විමසීම. මෙය බොහෝ අවස්ථාවන්හි භාවිතා වන පොදු රටාවකි. ඔබට කිරීමට අවශ්‍ය වන්නේ සේවක නූල් වලින් ප්‍රගති තොරතුරු ප්‍රදර්ශනය කිරීම පමණක් බැවින් මාෂල් විසඳුමට මෙම විසඳුම හොඳ විකල්පයක් බව ඔබට පෙනී යනු ඇතැයි මම සිතමි. එයට පහත වාසි ඇත.

  • යූඅයි සහ සේවක නූල් තදින් බැඳී ඇති ප්‍රවේශයට Control.Invokeහෝ Control.BeginInvokeප්‍රවේශයට පටහැනිව ලිහිල්ව බැඳී ඇත.
  • UI නූල් සේවක නූල් වල ප්‍රගතියට බාධාවක් නොවේ.
  • UI නූල් යාවත්කාලීන කිරීම සඳහා වැය කරන කාලය සේවක නූල් වලට ආධිපත්‍යය දැරීමට නොහැකිය.
  • UI සහ සේවක නූල් මඟින් මෙහෙයුම් සිදුකරන කාල පරතරයන් ස්වාධීනව පැවතිය හැකිය.
  • සේවක නූල් වලට UI නූල් පණිවිඩ පොම්පය අභිබවා යා නොහැක.
  • UI නූල් යූඅයි යාවත්කාලීන වන්නේ කවදාද සහ කොපමණ වාර ගණනක්ද යන්න නියම කරයි.

3
හොඳ අදහස. ඔබ සඳහන් නොකළ එකම දෙය නම් වර්කර් ත්‍රෙඩ් අවසන් වූ පසු ඔබ ටයිමරය නිසි ලෙස බැහැර කරන ආකාරයයි. යෙදුම අවසන් වූ විට මෙය කරදරයක් විය හැකි බව සලකන්න (එනම් පරිශීලකයා යෙදුම වසා දමයි). මෙය විසඳන්නේ කෙසේදැයි ඔබට අදහසක් තිබේද?
මැට්

AttMatt Elapsedසිදුවීම සඳහා නිර්නාමික හසුරුවන්නෙකු භාවිතා කරනවා වෙනුවට , ඔබ සාමාජික ක්‍රමයක් භාවිතා කරන අතර එමඟින් පෝරමය බැහැර කරන විට
ටයිමරය

@ ෆිල් 1990 - හොඳ කරුණක්. ඔබ අදහස් කළේ System.Timers.ElapsedEventHandler handler = (s, a) => { MyProgressLabel.Text = m_Text; };එය හරහා පැවරීම m_Timer.Elapsed += handler;, පසුව බැහැර කිරීමේ සන්දර්භය තුළ m_Timer.Elapsed -= handler;මම නිවැරදිද? මෙහි සාකච්ඡා කර ඇති උපදෙස් අනුව බැහැර කිරීම / වසා දැමීම සඳහා .
මැට්

28

පෙර පිළිතුරු වල ඉන්වොක් දේවල් කිසිවක් අවශ්‍ය නොවේ.

ඔබ WindowsFormsSynchronizationContext දෙස බැලිය යුතුය:

// In the main thread
WindowsFormsSynchronizationContext mUiContext = new WindowsFormsSynchronizationContext();

...

// In some non-UI Thread

// Causes an update in the GUI thread.
mUiContext.Post(UpdateGUI, userData);

...

void UpdateGUI(object userData)
{
    // Update your GUI controls here
}

4
ඔබ සිතන්නේ පෝස්ට් ක්‍රමය කබාය යටතේ භාවිතා කරන්නේ කුමක්ද? :)
ඇදහිය නොහැකි ලෙස

24

මෙය .NET Framework 3.0 භාවිතා කරමින් ඉහත විසඳුමට සමාන ය, නමුත් එය සම්පාදක-කාලීන ආරක්‍ෂිත සහාය පිළිබඳ ගැටළුව විසඳීය .

public  static class ControlExtension
{
    delegate void SetPropertyValueHandler<TResult>(Control souce, Expression<Func<Control, TResult>> selector, TResult value);

    public static void SetPropertyValue<TResult>(this Control source, Expression<Func<Control, TResult>> selector, TResult value)
    {
        if (source.InvokeRequired)
        {
            var del = new SetPropertyValueHandler<TResult>(SetPropertyValue);
            source.Invoke(del, new object[]{ source, selector, value});
        }
        else
        {
            var propInfo = ((MemberExpression)selector.Body).Member as PropertyInfo;
            propInfo.SetValue(source, value, null);
        }
    }
}

භාවිතා කිරීමට:

this.lblTimeDisplay.SetPropertyValue(a => a.Text, "some string");
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, false);

පරිශීලකයා වැරදි දත්ත වර්ගය සමත් වුවහොත් සම්පාදකයා අසමත් වේ.

this.lblTimeDisplay.SetPropertyValue(a => a.Visible, "sometext");

24

සැල්වෙට්! මෙම ප්‍රශ්නය සෙවීමෙන් පසුව, ෆ්‍රෑන්ක් ජී සහ ඔරිගන් හොස්ට්ගේ පිළිතුරු මට වඩාත්ම ප්‍රයෝජනවත් බව මට පෙනී ගියේය. දැන්, මම විෂුවල් බේසික් හි කේත කර මෙම ස්නිපටය පරිවර්තකයක් හරහා ධාවනය කළෙමි; ඒ නිසා එය සිදුවන්නේ කෙසේදැයි මට හරියටම විශ්වාස නැත.

මා සතුව සංවාද පෝරමයක් form_Diagnostics,ඇත, එය පොහොසත් පෙළ කොටුවක් ඇත, updateDiagWindow,එය මා භාවිතා කරන්නේ එක්තරා ආකාරයක ල ging ු- සටහන් සංදර්ශකයක් ලෙසය. සියළුම නූල් වලින් එහි පෙළ යාවත්කාලීන කිරීමට මට අවශ්‍ය විය. අමතර රේඛා කවුළුව ස්වයංක්‍රීයව නවතම රේඛාවලට අනුචලනය කිරීමට ඉඩ දෙයි.

ඒ නිසා, මට දැන් දර්ශනය එක පේළියකින් යාවත්කාලීන කළ හැකිය, සමස්ත වැඩසටහනේ ඕනෑම තැනක සිට එය නූල් රහිතව ක්‍රියාත්මක වේ යැයි ඔබ සිතන ආකාරයට:

  form_Diagnostics.updateDiagWindow(whatmessage);

ප්‍රධාන කේතය (මෙය ඔබගේ පෝරමයේ පන්ති කේතයේ ඇතුලත තබන්න):

#region "---------Update Diag Window Text------------------------------------"
// This sub allows the diag window to be updated by all threads
public void updateDiagWindow(string whatmessage)
{
    var _with1 = diagwindow;
    if (_with1.InvokeRequired) {
        _with1.Invoke(new UpdateDiagDelegate(UpdateDiag), whatmessage);
    } else {
        UpdateDiag(whatmessage);
    }
}
// This next line makes the private UpdateDiagWindow available to all threads
private delegate void UpdateDiagDelegate(string whatmessage);
private void UpdateDiag(string whatmessage)
{
    var _with2 = diagwindow;
    _with2.appendtext(whatmessage);
    _with2.SelectionStart = _with2.Text.Length;
    _with2.ScrollToCaret();
}
#endregion

21

බොහෝ අරමුණු සඳහා එය මේ තරම් සරල ය:

public delegate void serviceGUIDelegate();
private void updateGUI()
{
  this.Invoke(new serviceGUIDelegate(serviceGUI));
}

"serviceGUI ()" යනු ඔබට අවශ්‍ය තරම් පාලනයන් වෙනස් කළ හැකි (මෙය) ආකෘතිය තුළ ඇති GUI මට්ටමේ ක්‍රමයකි. අනෙක් නූල් වලින් "updateGUI ()" අමතන්න. සම්මත අගයන් සඳහා පරාමිතීන් එකතු කළ හැකිය, නැතහොත් (බොහෝ විට වේගවත්) අවශ්‍ය පරිදි පන්ති විෂය පථ විචල්‍යයන් අගුල් සහිතව භාවිතා කරන්න. GUI නොවන නූල් කාලය තීරණාත්මක නම් ඉන්වොයික් වෙනුවට BeginInvoke භාවිතා කරන්න (බ්‍රයන් ගිඩියොන්ගේ අනතුරු ඇඟවීම මතකයේ තබා ගන්න).


21

මෙය මගේ C # 3.0 ඉයන් කෙම්ප්ගේ විසඳුමේ විචලනය:

public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control
{
    var memberExpression = property.Body as MemberExpression;
    if (memberExpression == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    if (control.InvokeRequired)
        control.Invoke(
            (Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread,
            new object[] { control, property, value }
        );
    else
        propertyInfo.SetValue(control, value, null);
}

ඔබ එය මේ ආකාරයට හඳුන්වයි:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. එය "MemberExpression ලෙස" ප්‍රති result ලයට ශුන්‍ය පරීක්‍ෂණයක් එක් කරයි.
  2. එය ස්ථිතික වර්ගය-ආරක්ෂාව වැඩි දියුණු කරයි.

එසේ නොමැති නම්, මුල් පිටපත ඉතා හොඳ විසඳුමකි.


21
Label lblText; //initialized elsewhere

void AssignLabel(string text)
{
   if (InvokeRequired)
   {
      BeginInvoke((Action<string>)AssignLabel, text);
      return;
   }

   lblText.Text = text;           
}

සටහනට BeginInvoke()වඩා වැඩි කැමැත්තක් Invoke()දක්වන්නේ එය අවහිරතා ඇතිවීමට ඇති ඉඩකඩ අඩු නිසාය (කෙසේ වෙතත්, ලේබලයකට පෙළ පැවරීමේදී මෙය මෙහි ගැටළුවක් නොවේ):

භාවිතා කරන විට Invoke()ඔබ නැවත පැමිණෙන ක්‍රමය එනතෙක් බලා සිටී. දැන්, ඔබ ත්‍රෙඩ් එක සඳහා රැඳී සිටීමට අවශ්‍ය වන ආයාචිත කේතයේ යමක් කළ හැකි අතර, එය ඔබ අමතන සමහර ක්‍රියාකාරකම් වල තැන්පත් කර ඇත්නම් එය ක්ෂණිකව නොපෙනේ, එය සිදුවීම් හසුරුවන්නන් හරහා වක්‍රව සිදුවිය හැකිය. එබැවින් ඔබ නූල් එක එනතුරු බලා සිටිනු ඇත, නූල් ඔබ එනතුරු බලා සිටින අතර ඔබ අවහිර වී ඇත.

මෙය ඇත්ත වශයෙන්ම අප විසින් නිකුත් කරන ලද සමහර මෘදුකාංග එල්ලීමට හේතු විය. ආදේශ කිරීමෙන් එය නිවැරදි කිරීමට තරම් පහසු Invoke()විය BeginInvoke(). ඔබට සමමුහුර්ත ක්‍රියාකාරිත්වයේ අවශ්‍යතාවයක් නොමැති නම්, ඔබට ප්‍රතිලාභ අගයක් අවශ්‍ය නම් එය භාවිතා කරන්න BeginInvoke().


20

මට එම ගැටලුවටම මුහුණ දුන් විට මම ගූගල් වෙතින් උදව් පැතුවෙමි, නමුත් මට සරල විසඳුමක් ලබා දෙනවාට වඩා එය නිදසුන් MethodInvokerහා බ්ලා බ්ලා බ්ලා සඳහා උදාහරණ ලබා දීමෙන් මා ව්‍යාකූල කළේය. එබැවින් මම එය තනිවම විසඳීමට තීරණය කළෙමි. මෙන්න මගේ විසඳුම:

මේ වගේ නියෝජිතයෙක් කරන්න:

Public delegate void LabelDelegate(string s);

void Updatelabel(string text)
{
   if (label.InvokeRequired)
   {
       LabelDelegate LDEL = new LabelDelegate(Updatelabel);
       label.Invoke(LDEL, text);
   }
   else
       label.Text = text
}

ඔබට මෙම ශ්‍රිතය මේ වගේ නව ත්‍රෙඩ් එකකින් ඇමතිය හැකිය

Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();

පටලවා නොගන්න Thread(() => .....). මම නූල් එකක වැඩ කරන විට මම නිර්නාමික ශ්‍රිතයක් හෝ ලැම්බඩා ප්‍රකාශනයක් භාවිතා කරමි. කේත රේඛා අඩු කිරීම සඳහා ThreadStart(..)මා මෙහි විස්තර කිරීමට බලාපොරොත්තු නොවන ක්‍රමයද භාවිතා කළ හැකිය .


18

මේ වගේ දෙයක් භාවිතා කරන්න:

 this.Invoke((MethodInvoker)delegate
            {
                progressBar1.Value = e.ProgressPercentage; // runs on UI thread
            });

ඔබ සතුව ඇත්නම් e.ProgressPercentage, ඔබ මෙය හඳුන්වන ක්‍රමයෙන් දැනටමත් UI ත්‍රෙඩ් එකේ නැද්ද?
ලාර්ස්ටෙක්

ProgressChanged සිද්ධිය UI නූල් මත ධාවනය වේ. එය BackgroundWorker භාවිතා කිරීමේ පහසුවකි. සම්පුර්ණ කරන ලද සිදුවීම ගුයි මත ද ක්‍රියාත්මක වේ. UI නොවන නූල් වල ධාවනය වන එකම දෙය DoWork ක්‍රමයයි.
ලාර්ස්ටෙක්

15

ඔබට දැනටමත් පවතින නියෝජිතයා භාවිතා කළ හැකිය Action:

private void UpdateMethod()
{
    if (InvokeRequired)
    {
        Invoke(new Action(UpdateMethod));
    }
}

14

මගේ අනුවාදය වන්නේ පුනරාවර්තන "මන්ත්‍රයේ" එක් පේළියක් ඇතුළත් කිරීමයි:

තර්ක සඳහා:

    void Aaaaaaa()
    {
        if (InvokeRequired) { Invoke(new Action(Aaaaaaa)); return; } //1 line of mantra

        // Your code!
    }

තර්ක ඇති ශ්‍රිතයක් සඳහා:

    void Bbb(int x, string text)
    {
        if (InvokeRequired) { Invoke(new Action<int, string>(Bbb), new[] { x, text }); return; }
        // Your code!
    }

එය තොරතුරු තාක්‍ෂණයයි .


සමහර තර්ක : සාමාන්‍යයෙන් කේත කියවීමේ හැකියාව if ()එක් පේළියක ප්‍රකාශයකින් පසුව put put තැබීම නරක ය . නමුත් මේ අවස්ථාවේ දී එය එකම "මන්ත්‍රය" වේ. මෙම ක්‍රමය ව්‍යාපෘතියට අනුකූල නම් එය කේත කියවීමේ හැකියාව බිඳ දමන්නේ නැත. තවද එය ඔබේ කේතය පැටව් ගසමින් සුරකිනු ඇත (කේත පහක් වෙනුවට එක් පේළියක්).

ඔබ දකින පරිදි if(InvokeRequired) {something long}"මෙම ක්‍රියාව වෙනත් ත්‍රෙඩ් එකකින් ඇමතීම ආරක්ෂිතයි".


13

මෙය භාවිතා කර ලේබලය නැවුම් කිරීමට උත්සාහ කරන්න

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate() { };

    public static void Refresh(this UIElement uiElement)
    {
        uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }
}

එය වින්ඩෝස් පෝරම සඳහාද ?
කික්නෙට්

13

පන්ති විචල්‍යයක් සාදන්න:

SynchronizationContext _context;

ඔබේ UI නිර්මාණය කරන ඉදිකිරීම්කරු තුළ එය සකසන්න:

var _context = SynchronizationContext.Current;

ඔබට ලේබලය යාවත්කාලීන කිරීමට අවශ්‍ය විට:

_context.Send(status =>{
    // UPDATE LABEL
}, null);

12

ඔබ ආයාචනය සහ නියෝජිතයා භාවිතා කළ යුතුය

private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });

12

අනෙක් බොහෝ පිළිතුරු මෙම ප්‍රශ්නයට මට ටිකක් සංකීර්ණයි (මම C # ට අලුත්), එබැවින් මම මගේ ලියන්නේ:

මට WPF අයදුම්පතක් ඇති අතර සේවකයෙකු පහත පරිදි අර්ථ දක්වා ඇත:

නිකුත් කිරීම:

BackgroundWorker workerAllocator;
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1) {
    // This is my DoWork function.
    // It is given as an anonymous function, instead of a separate DoWork function

    // I need to update a message to textbox (txtLog) from this thread function

    // Want to write below line, to update UI
    txt.Text = "my message"

    // But it fails with:
    //  'System.InvalidOperationException':
    //  "The calling thread cannot access this object because a different thread owns it"
}

විසඳුමක්:

workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1)
{
    // The below single line works
    txtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}

ඉහත පේළියේ තේරුම කුමක්දැයි මම තවම සොයාගෙන නැත, නමුත් එය ක්‍රියාත්මක වේ.

සඳහා WinForms :

විසඳුමක්:

txtLog.Invoke((MethodInvoker)delegate
{
    txtLog.Text = "my message";
});

ප්‍රශ්නය වූයේ ඩබ්ලිව්පීඑෆ් ගැන නොව වින්ෆෝම්ස් ගැන ය.
මාක් එල්

ස්තූතියි. ඉහත WinForms විසඳුම එක් කරන ලදි.
මනෝහර් රෙඩ්ඩි පෝරෙඩි

... එය මෙම ප්‍රශ්නයේම තවත් බොහෝ පිළිතුරු වල පිටපතක් පමණි, නමුත් හරි. විසඳුමේ කොටසක් නොවී ඔබේ පිළිතුර මකා දැමිය යුත්තේ ඇයි?
මාක් එල්

හ්ම්, ඔබ නිවැරදි නම්, ඔබ මගේ පිළිතුර අවධානයෙන් කියවා, ආරම්භක කොටස (මම පිළිතුර ලිවීමට හේතුව), සහ තව ටිකක් අවධානයෙන් ඔබ දකින්නේ හරියටම එකම ගැටලුවක් ඇති සහ අද දින ඉහළ නංවා ඇති අයෙකු සිටින බවයි. මගේ සරල පිළිතුර, සහ ඊටත් වඩා අවධානයෙන් ඔබට මේ සියල්ල සිදු වූයේ ඇයිද යන්න පිළිබඳ සත්‍ය කථාව කල්තියා දැනගත හැකි නම්, මම wpf සොයන විට පවා ගූගල් මාව මෙහි යවයි. නිසැකවම ඔබට මෙම අඩු හෝ වැඩි වශයෙන් පැහැදිලි හේතු 3 ක් මග හැරී ඇති හෙයින්, ඔබේ පහත් අගය ඉවත් නොකරන්නේ මන්දැයි මට තේරුම් ගත හැකිය. හරි එක පිරිසිදු කරනවා වෙනුවට, වඩා දුෂ්කර නව දෙයක් සාදන්න.
මනෝහර් රෙඩ්ඩි පෝරෙඩි


8

උදාහරණයක් ලෙස, වත්මන් නූල් හැර වෙනත් පාලනයක් වෙත පිවිසෙන්න:

Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{
    lblThreshold.Text = Speed_Threshold.ToString();
}));

එහි lblThresholdලේබලයක් ඇති අතර Speed_Thresholdඑය ගෝලීය විචල්‍යයකි.


8

ඔබ UI ත්‍රෙඩ් එකේ සිටින විට ඔබට එහි සමමුහුර්තකරණ සන්දර්භය කාර්ය කාලසටහන ඉල්ලිය හැකිය. එය ඔබට UI ත්‍රෙඩ් එකේ සෑම දෙයක්ම උපලේඛනගත කරන කාර්ය සාධක උපලේඛනයක් ලබා දෙනු ඇත.

එවිට ඔබට ඔබේ කාර්යයන් දම්වැල් කළ හැකි වන අතර එමඟින් ප්‍රති result ලය සුදානම් වන විට තවත් කාර්යයක් (UI නූල් මත සැලසුම් කර ඇත) එය තෝරාගෙන එය ලේබලයකට පවරයි.

public partial class MyForm : Form
{
  private readonly TaskScheduler _uiTaskScheduler;
  public MyForm()
  {
    InitializeComponent();
    _uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
  }

  private void buttonRunAsyncOperation_Click(object sender, EventArgs e)
  {
    RunAsyncOperation();
  }

  private void RunAsyncOperation()
  {
    var task = new Task<string>(LengthyComputation);
    task.ContinueWith(antecedent =>
                         UpdateResultLabel(antecedent.Result), _uiTaskScheduler);
    task.Start();
  }

  private string LengthyComputation()
  {
    Thread.Sleep(3000);
    return "47";
  }

  private void UpdateResultLabel(string text)
  {
    labelResult.Text = text;
  }
}

මෙය සමගාමී කේත ලිවීමේ වඩාත් කැමති ක්‍රමය වන කාර්යයන් සඳහා (නූල් නොව) ක්‍රියා කරයි .


1
කරන මා ෙවත පවරා Task.Startසාමාන්යයෙන් හොඳ සිරිතක් නොවන blogs.msdn.com/b/pfxteam/archive/2012/01/14/10256832.aspx
Ohad ෂ්නයිඩර්

8

මම පිළිතුරු කියවා ඇති අතර මෙය ඉතා උණුසුම් මාතෘකාවක් ලෙස පෙනේ. මම දැනට .NET 3.5 SP1 සහ Windows ආකෘති භාවිතා කරමි.

InvokeRequired භාවිතා කරන පෙර පිළිතුරු වල බොහෝ සෙයින් විස්තර කර ඇති සුප්‍රසිද්ධ සූත්‍රය දේපල අවස්ථාවන් ආවරණය කරයි, නමුත් සමස්ත නොවේ.

දේ නම් හැසිරවිය තවමත් නිර්මාණය වී නොමැති බවත්;

මෙහි විස්තර කර ඇති පරිදි InvokeRequired දේපල (MSDN සඳහා Control.InvokeRequired දේපල යොමු කිරීම) සත්‍ය වන්නේ ඇමතුම GUI නූල් නොවන නූල් වලින් නම්, අසත්‍යය GUI නූල් වලින් ඇමතුමක් ලැබුනේ නම් හෝ හසුරුවන්නේ නම් තවම නිර්මාණය කර නැත.

ඔබට වෙනත් ත්‍රෙඩ් එකකින් මොඩල් පෝරමයක් පෙන්වීමට හා යාවත්කාලීන කිරීමට අවශ්‍ය නම් ඔබට ව්‍යතිරේකයක් හමුවිය හැකිය. ඔබට එම පෝරමය විධිමත් ලෙස පෙන්වීමට අවශ්‍ය නිසා, ඔබට පහත සඳහන් දෑ කළ හැකිය:

private MyForm _gui;

public void StartToDoThings()
{
    _gui = new MyForm();
    Thread thread = new Thread(SomeDelegate);
    thread.Start();
    _gui.ShowDialog();
}

තවද නියෝජිතයාට GUI හි ලේබලයක් යාවත්කාලීන කළ හැකිය:

private void SomeDelegate()
{
    // Operations that can take a variable amount of time, even no time
    //... then you update the GUI
    if(_gui.InvokeRequired)
        _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });
    else
        _gui.Label1.Text = "Done!";
}

මෙය ඇති විය හැක InvalidOperationException එය නිර්මාණය සඳහා GUI නූල් සඳහා ගතවන කාලය (වඩා එය කියවා සරල ලෙස එය අර්ථ නිරූපනය) ලේබලය හි යාවත්කාලීන කිරීමට පෙර මෙහෙයුම් "අඩු කාලයක් ගත" නම් පත්රය ගේ හැසිරවිය . ShowDialog () තුළ මෙය සිදු වේ ක්‍රමය තුළ ය.

ඔබ මේ ආකාරයට හැසිරවීම සඳහාද පරීක්ෂා කළ යුතුය :

private void SomeDelegate()
{
    // Operations that can take a variable amount of time, even no time
    //... then you update the GUI
    if(_gui.IsHandleCreated)  //  <---- ADDED
        if(_gui.InvokeRequired)
            _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });
        else
            _gui.Label1.Text = "Done!";
}

නම් ඔබට ඉටු කිරීමේ මෙහෙයුම හැසිරවිය හැකි හැසිරවිය (ඉහත කේතය පෙන්වා වැනි) ඔබ පමණක් චිත්රක යාවත්කාලීන නොසලකා හැරිය හැක, හෝ ඔබ (වැඩි අවදානම් සහිත) බලා ඉන්න පුළුවන්: තවමත් නිර්මාණය කර නොමැති. මෙය ප්‍රශ්නයට පිළිතුරු සැපයිය යුතුය.

වෛකල්පිත දේවල්: පුද්ගලිකව මම පහත සඳහන් කේතයන් ඉදිරිපත් කළෙමි:

public class ThreadSafeGuiCommand
{
  private const int SLEEPING_STEP = 100;
  private readonly int _totalTimeout;
  private int _timeout;

  public ThreadSafeGuiCommand(int totalTimeout)
  {
    _totalTimeout = totalTimeout;
  }

  public void Execute(Form form, Action guiCommand)
  {
    _timeout = _totalTimeout;
    while (!form.IsHandleCreated)
    {
      if (_timeout <= 0) return;

      Thread.Sleep(SLEEPING_STEP);
      _timeout -= SLEEPING_STEP;
    }

    if (form.InvokeRequired)
      form.Invoke(guiCommand);
    else
      guiCommand();
  }
}

මෙම ThreadSafeGuiCommand හි නිදසුනක් සමඟ වෙනත් ත්‍රෙඩ් එකකින් යාවත්කාලීන වන මගේ පෝරම පෝෂණය කරන අතර, GUI යාවත්කාලීන කරන ක්‍රම (මගේ පෝරමයේ) මම අර්ථ දක්වන්නෙමි :

public void SetLabeTextTo(string value)
{
  _threadSafeGuiCommand.Execute(this, delegate { Label1.Text = value; });
}

මේ ආකාරයෙන් මට විශ්වාසයි, ඇමතුම ලබා දෙන ඕනෑම ත්‍රෙඩ් එකක් මගේ GUI යාවත්කාලීන කර ඇති බව, විකල්පයක් ලෙස මනාව නිර්වචනය කරන ලද කාලයක් (කල් ඉකුත් වීම) සඳහා බලා සිටී.


1
මෙය සොයා ගැනීම සඳහා මෙහි පැමිණියේ, මම ද IsHandleCreated සඳහා පරීක්ෂා කර බලන බැවිනි. පරීක්ෂා කළ යුතු තවත් දේපලක් වන්නේ IsDisposed. ඔබගේ පෝරමය බැහැර කර ඇත්නම්, ඔබට ඒ සඳහා ඉන්වොක් () අමතන්න බැහැ. ඔබගේ පසුබිම් නූල් සම්පුර්ණ වීමට පෙර පරිශීලකයා පෝරමය වසා දැමුවේ නම්, පෝරමය බැහැර කරන විට එය නැවත UI වෙත ඇමතීමට උත්සාහ කිරීම අවශ්‍ය නොවේ.
ජෝන්

ආරම්භ කිරීම නරක අදහසක් යැයි මම කියමි ... සාමාන්‍යයෙන්, ඔබ වහාම ළමා පෝරමය පෙන්වන අතර පසුබිම් සැකසීමේදී ප්‍රගති තීරුවක් හෝ වෙනත් ප්‍රතිපෝෂණයක් ලබා දෙනු ඇත. නැතහොත් ඔබ මුලින් සියලු සැකසුම් සිදු කර ප්‍රති .ලය නිර්මාණය කිරීමේදී නව ආකෘතියට යොමු කරයි. දෙකම එකවර සිදු කිරීමෙන් සාමාන්‍යයෙන් ආන්තික ප්‍රතිලාභ ලැබිය හැකි නමුත් නඩත්තු කළ හැකි කේතයක් අඩුය.
Phil1970

විස්තර කරන ලද දර්ශනය පසුබිම්-නූල් රැකියාවක ප්‍රගති දර්ශනයක් ලෙස භාවිතා කරන මෝඩමය ආකාරයක් සැලකිල්ලට ගනී . එය මෝඩමය විය යුතු බැවින්, එය Form.ShowDialog () ක්‍රමය ඇමතීමෙන් පෙන්විය යුතුය . මෙය කිරීමෙන්, පෝරමය වසා දමන තුරු ඇමතුම ක්‍රියාත්මක කිරීමෙන් පසුව ඔබේ කේතය ක්‍රියාත්මක කිරීම වළක්වයි. එබැවින්, ලබා දී ඇති උදාහරණයට වඩා වෙනස්ව ඔබට පසුබිම් නූල් ආරම්භ කළ නොහැකි නම් (සහ, ඇත්ත වශයෙන්ම, ඔබට හැකිය) පසුබිම් නූල් ආරම්භ වූ පසු මෙම පෝරමය ක්‍රමයෙන් පෙන්විය යුතුය. මෙම අවස්ථාවේදී, හසුරුව නිර්මාණය කිරීම සඳහා ඔබ පරීක්ෂා කළ යුතුය. ඔබට මොඩල් පෝරමයක් අවශ්‍ය නොවේ නම් එය තවත් කතාවකි.
සුමේ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.