Cross thread ошибка

  • Remove From My Forums
  • Вопрос

  • Здравствуйте! Возникла довольно интересная проблема. Суть: на форме есть datagridview, который наполняем из MySql и есть backgroundwoker, собственно посредствам него запускаем процесс наполнения. При нажатии клавиш ctrl+F5, не происходит никаких ошибок и
    форма наполняется без проблем! Однако! если запустить Debugger, то выскочит ошибка:

    Cross-thread operation not valid: Control » accessed from a thread other than the thread it was created on. Проходим по ссылке предложенной студией, ошибки в коде не находим.

    Как подобное можно исправить?

    • Перемещено

      1 октября 2010 г. 21:38
      MSDN Forums consolidation (От:Visual C#)

Ответы

  • Код, работающий с контролами, должен выполняться в основном потоке. Для переброса куска кода в основной поток есть метод
    Control.Invoke. Можно использовать стандартный делегат MethodInvoker в виде this.Invoke(new MethodInvoker(delegate{ /* кусок кода для выполнения*/ }));

    Ограничение введено чтобы избежать непредсказуемых багов. Например, при отрисовке — грид начнет рисоваться с 3 строками, а к концу отрисовки их станет 5 :)  

    Кстати, поиск даже на этом форуме выдает 600+ результатов по тексту исключения ;)

    • Предложено в качестве ответа
      PashaPash
      31 июля 2010 г. 14:09
    • Помечено в качестве ответа
      DmitrijN
      31 июля 2010 г. 14:27

  • Спасибо за Ваш ответ!

    Скажу честно что по этому форуму поиском не пользовался=( Моя ошибка… воспользовался яндексом и нашел такое решение this.dataGridView1.Invoke(new Action(() => { this.dataGridView1.Rows.Add(arr); })); Тоже работает.

    • Помечено в качестве ответа
      DmitrijN
      31 июля 2010 г. 14:27

I have a scenario. (Windows Forms, C#, .NET)

  1. There is a main form which hosts some user control.
  2. The user control does some heavy data operation, such that if I directly call the UserControl_Load method the UI become nonresponsive for the duration for load method execution.
  3. To overcome this I load data on different thread (trying to change existing code as little as I can)
  4. I used a background worker thread which will be loading the data and when done will notify the application that it has done its work.
  5. Now came a real problem. All the UI (main form and its child usercontrols) was created on the primary main thread. In the LOAD method of the usercontrol I’m fetching data based on the values of some control (like textbox) on userControl.

The pseudocode would look like this:

CODE 1

UserContrl1_LoadDataMethod()
{
    if (textbox1.text == "MyName") // This gives exception
    {
        //Load data corresponding to "MyName".
        //Populate a globale variable List<string> which will be binded to grid at some later stage.
    }
}

The Exception it gave was

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

To know more about this I did some googling and a suggestion came up like using the following code

CODE 2

UserContrl1_LoadDataMethod()
{
    if (InvokeRequired) // Line #1
    {
        this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
        return;
    }

    if (textbox1.text == "MyName") // Now it won't give an exception
    {
    //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be binded to grid at some later stage
    }
}

But it still seems that I’ve come back to square one. The Application again
becomes unresponsive. It seems to be due to the execution of line #1 if condition. The loading task is again done by the parent thread and not the third that I spawned.

I don’t know whether I perceived this right or wrong.

How do I resolve this and also what is the effect of execution of Line#1 if block?

The situation is this: I want to load data into a global variable based on the value of a control. I don’t want to change the value of a control from the child thread. I’m not going to do it ever from a child thread.

So only accessing the value so that the corresponding data can be fetched from the database.

starball's user avatar

starball

14.8k6 gold badges26 silver badges127 bronze badges

asked Sep 26, 2008 at 21:12

Prerak K's user avatar

1

As per Prerak K’s update comment (since deleted):

I guess I have not presented the question properly.

Situation is this: I want to load data into a global variable based on the value of a control. I don’t want to change the value of a control from the child thread. I’m not going to do it ever from a child thread.

So only accessing the value so that corresponding data can be fetched from the database.

The solution you want then should look like:

UserContrl1_LOadDataMethod()
{
    string name = "";
    if(textbox1.InvokeRequired)
    {
        textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
    }
    if(name == "MyName")
    {
        // do whatever
    }
}

Do your serious processing in the separate thread before you attempt to switch back to the control’s thread. For example:

UserContrl1_LOadDataMethod()
{
    if(textbox1.text=="MyName") //<<======Now it wont give exception**
    {
        //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be
        //bound to grid at some later stage
        if(InvokeRequired)
        {
            // after we've done all the processing, 
            this.Invoke(new MethodInvoker(delegate {
                // load the control with the appropriate data
            }));
            return;
        }
    }
}

Community's user avatar

answered Sep 26, 2008 at 21:22

Jeff Hubbard's user avatar

Jeff HubbardJeff Hubbard

9,7823 gold badges30 silver badges28 bronze badges

2

You only want to use Invoke or BeginInvoke for the bare minimum piece of work required to change the UI. Your «heavy» method should execute on another thread (e.g. via BackgroundWorker) but then using Control.Invoke/Control.BeginInvoke just to update the UI. That way your UI thread will be free to handle UI events etc.

See my threading article for a WinForms example — although the article was written before BackgroundWorker arrived on the scene, and I’m afraid I haven’t updated it in that respect. BackgroundWorker merely simplifies the callback a bit.

answered Sep 26, 2008 at 21:21

Jon Skeet's user avatar

Jon SkeetJon Skeet

1.4m861 gold badges9098 silver badges9172 bronze badges

4

I know its too late now. However even today if you are having trouble accessing cross thread controls? This is the shortest answer till date :P

Invoke(new Action(() =>
                {
                    label1.Text = "WooHoo!!!";
                }));

This is how i access any form control from a thread.

answered Oct 17, 2017 at 11:55

Bravo's user avatar

BravoBravo

3,3014 gold badges27 silver badges43 bronze badges

2

I have had this problem with the FileSystemWatcher and found that the following code solved the problem:

fsw.SynchronizingObject = this

The control then uses the current form object to deal with the events, and will therefore be on the same thread.

Druid's user avatar

Druid

6,4034 gold badges40 silver badges56 bronze badges

answered Jan 13, 2009 at 2:13

Peter C's user avatar

Peter CPeter C

2,2471 gold badge25 silver badges28 bronze badges

1

I find the check-and-invoke code which needs to be littered within all methods related to forms to be way too verbose and unneeded. Here’s a simple extension method which lets you do away with it completely:

public static class Extensions
{
    public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del) 
        where TControlType : Control
        {
            if (control.InvokeRequired)
                control.Invoke(new Action(() => del(control)));
            else
                del(control);
    }
}

And then you can simply do this:

textbox1.Invoke(t => t.Text = "A");

No more messing around — simple.

answered May 2, 2016 at 13:42

Rob's user avatar

RobRob

27.3k16 gold badges82 silver badges97 bronze badges

0

Controls in .NET are not generally thread safe. That means you shouldn’t access a control from a thread other than the one where it lives. To get around this, you need to invoke the control, which is what your 2nd sample is attempting.

However, in your case all you’ve done is pass the long-running method back to the main thread. Of course, that’s not really what you want to do. You need to rethink this a little so that all you’re doing on the main thread is setting a quick property here and there.

Peter Mortensen's user avatar

answered Sep 26, 2008 at 21:19

Joel Coehoorn's user avatar

Joel CoehoornJoel Coehoorn

396k113 gold badges565 silver badges792 bronze badges

0

Follow the simplest (in my opinion) way to modify objects from another thread:

using System.Threading.Tasks;
using System.Threading;

namespace TESTE
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Action<string> DelegateTeste_ModifyText = THREAD_MOD;
            Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");
        }

        private void THREAD_MOD(string teste)
        {
            textBox1.Text = teste;
        }
    }
}

answered May 6, 2016 at 16:49

Vanderley Maia's user avatar

0

A new look using Async/Await and callbacks. You only need one line of code if you keep the extension method in your project.

/// <summary>
/// A new way to use Tasks for Asynchronous calls
/// </summary>
public class Example
{
    /// <summary>
    /// No more delegates, background workers etc. just one line of code as shown below
    /// Note it is dependent on the XTask class shown next.
    /// </summary>
    public async void ExampleMethod()
    {
        //Still on GUI/Original Thread here
        //Do your updates before the next line of code
        await XTask.RunAsync(() =>
        {
            //Running an asynchronous task here
            //Cannot update GUI Thread here, but can do lots of work
        });
        //Can update GUI/Original thread on this line
    }
}

/// <summary>
/// A class containing extension methods for the Task class 
/// Put this file in folder named Extensions
/// Use prefix of X for the class it Extends
/// </summary>
public static class XTask
{
    /// <summary>
    /// RunAsync is an extension method that encapsulates the Task.Run using a callback
    /// </summary>
    /// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
    /// <returns></returns>
    public async static Task RunAsync(Action Code)
    {
        await Task.Run(() =>
        {
            Code();
        });
        return;
    }
}

You can add other things to the Extension method such as wrapping it in a Try/Catch statement, allowing caller to tell it what type to return after completion, an exception callback to caller:

Adding Try Catch, Auto Exception Logging and CallBack

    /// <summary>
    /// Run Async
    /// </summary>
    /// <typeparam name="T">The type to return</typeparam>
    /// <param name="Code">The callback to the code</param>
    /// <param name="Error">The handled and logged exception if one occurs</param>
    /// <returns>The type expected as a competed task</returns>

    public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error)
    {
       var done =  await Task<T>.Run(() =>
        {
            T result = default(T);
            try
            {
               result = Code("Code Here");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Unhandled Exception: " + ex.Message);
                Console.WriteLine(ex.StackTrace);
                Error(ex);
            }
            return result;

        });
        return done;
    }
    public async void HowToUse()
    {
       //We now inject the type we want the async routine to return!
       var result =  await RunAsync<bool>((code) => {
           //write code here, all exceptions are logged via the wrapped try catch.
           //return what is needed
           return someBoolValue;
       }, 
       error => {

          //exceptions are already handled but are sent back here for further processing
       });
        if (result)
        {
            //we can now process the result because the code above awaited for the completion before
            //moving to this statement
        }
    }

answered May 16, 2016 at 1:55

JWP's user avatar

JWPJWP

6,5923 gold badges47 silver badges73 bronze badges

This is not the recommended way to solve this error but you can suppress it quickly, it will do the job . I prefer this for prototypes or demos . add

CheckForIllegalCrossThreadCalls = false

in Form1() constructor .

answered May 10, 2017 at 11:02

Özgür's user avatar

ÖzgürÖzgür

8,0472 gold badges68 silver badges66 bronze badges

Here is an alternative way if the object you are working with doesn’t have

(InvokeRequired)

This is useful if you are working with the main form in a class other than the main form with an object that is in the main form, but doesn’t have InvokeRequired

delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);

private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
{
    MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
}

public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
{
    objectWithoutInvoke.Text = text;
}

It works the same as above, but it is a different approach if you don’t have an object with invokerequired, but do have access to the MainForm

answered May 24, 2012 at 22:49

Ashitakalax's user avatar

AshitakalaxAshitakalax

1,4271 gold badge18 silver badges26 bronze badges

I found a need for this while programming an iOS-Phone monotouch app controller in a visual studio winforms prototype project outside of xamarin stuidio. Preferring to program in VS over xamarin studio as much as possible, I wanted the controller to be completely decoupled from the phone framework. This way implementing this for other frameworks like Android and Windows Phone would be much easier for future uses.

I wanted a solution where the GUI could respond to events without the burden of dealing with the cross threading switching code behind every button click. Basically let the class controller handle that to keep the client code simple. You could possibly have many events on the GUI where as if you could handle it in one place in the class would be cleaner. I am not a multi theading expert, let me know if this is flawed.

public partial class Form1 : Form
{
    private ExampleController.MyController controller;

    public Form1()
    {          
        InitializeComponent();
        controller = new ExampleController.MyController((ISynchronizeInvoke) this);
        controller.Finished += controller_Finished;
    }

    void controller_Finished(string returnValue)
    {
        label1.Text = returnValue; 
    }

    private void button1_Click(object sender, EventArgs e)
    {
        controller.SubmitTask("Do It");
    }
}

The GUI form is unaware the controller is running asynchronous tasks.

public delegate void FinishedTasksHandler(string returnValue);

public class MyController
{
    private ISynchronizeInvoke _syn; 
    public MyController(ISynchronizeInvoke syn) {  _syn = syn; } 
    public event FinishedTasksHandler Finished; 

    public void SubmitTask(string someValue)
    {
        System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
    }

    private void submitTask(string someValue)
    {
        someValue = someValue + " " + DateTime.Now.ToString();
        System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.

        if (Finished != null)
        {
            if (_syn.InvokeRequired)
            {
                _syn.Invoke(Finished, new object[] { someValue });
            }
            else
            {
                Finished(someValue);
            }
        }
    }
}

cramopy's user avatar

cramopy

3,4596 gold badges27 silver badges42 bronze badges

answered Sep 11, 2013 at 18:29

RandallTo's user avatar

RandallToRandallTo

1712 silver badges8 bronze badges

Simple and re-usable way to work around this problem.

Extension Method

public static class FormExts
{
    public static void LoadOnUI(this Form frm, Action action)
    {
        if (frm.InvokeRequired) frm.Invoke(action);
        else action.Invoke();
    }
}

Sample Usage

private void OnAnyEvent(object sender, EventArgs args)
{
    this.LoadOnUI(() =>
    {
        label1.Text = "";
        button1.Text = "";
    });
}

answered May 8, 2020 at 18:44

Timothy Macharia's user avatar

Timothy MachariaTimothy Macharia

2,5811 gold badge20 silver badges26 bronze badges

2

Along the same lines as previous answers,
but a very short addition that Allows to use all Control properties without having cross thread invokation exception.

Helper Method

/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{
    if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
    {
        a();
    }));
    else return false;

    return true;
}

Sample Usage

// usage on textbox
public void UpdateTextBox1(String text)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
    textBox1.Text = ellapsed;
}

//Or any control
public void UpdateControl(Color c, String s)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
    myControl.Text = s;
    myControl.BackColor = c;
}

cramopy's user avatar

cramopy

3,4596 gold badges27 silver badges42 bronze badges

answered Oct 22, 2014 at 11:29

Mike's user avatar

MikeMike

1,1222 gold badges13 silver badges25 bronze badges

For example to get the text from a Control of the UI thread:

Private Delegate Function GetControlTextInvoker(ByVal ctl As Control) As String

Private Function GetControlText(ByVal ctl As Control) As String
    Dim text As String

    If ctl.InvokeRequired Then
        text = CStr(ctl.Invoke(
            New GetControlTextInvoker(AddressOf GetControlText), ctl))
    Else
        text = ctl.Text
    End If

    Return text
End Function

Jimi's user avatar

Jimi

28.9k8 gold badges42 silver badges60 bronze badges

answered Feb 5, 2014 at 12:06

UrsulRosu's user avatar

UrsulRosuUrsulRosu

4971 gold badge6 silver badges16 bronze badges

this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));

answered Nov 2, 2017 at 22:20

Hamid Jolany's user avatar

Same question : how-to-update-the-gui-from-another-thread-in-c

Two Ways:

  1. Return value in e.result and use it to set yout textbox value in backgroundWorker_RunWorkerCompleted event

  2. Declare some variable to hold these kind of values in a separate class (which will work as data holder) . Create static instance of this class adn you can access it over any thread.

Example:

public  class data_holder_for_controls
{
    //it will hold value for your label
    public  string status = string.Empty;
}

class Demo
{
    public static  data_holder_for_controls d1 = new data_holder_for_controls();
    static void Main(string[] args)
    {
        ThreadStart ts = new ThreadStart(perform_logic);
        Thread t1 = new Thread(ts);
        t1.Start();
        t1.Join();
        //your_label.Text=d1.status; --- can access it from any thread 
    }

    public static void perform_logic()
    {
        //put some code here in this function
        for (int i = 0; i < 10; i++)
        {
            //statements here
        }
        //set result in status variable
        d1.status = "Task done";
    }
}

boop_the_snoot's user avatar

answered Mar 25, 2017 at 4:50

Saurabh's user avatar

SaurabhSaurabh

1,5034 gold badges20 silver badges36 bronze badges

Simply use this:

this.Invoke((MethodInvoker)delegate
            {
                YourControl.Property= value; // runs thread safe
            });

answered Apr 28, 2019 at 20:56

Hasan Shouman's user avatar

Hasan ShoumanHasan Shouman

2,1161 gold badge20 silver badges26 bronze badges

Action y; //declared inside class

label1.Invoke(y=()=>label1.Text=»text»);

answered Dec 17, 2017 at 6:40

Antonio Leite's user avatar

There are two options for cross thread operations.

Control.InvokeRequired Property 

and second one is to use

SynchronizationContext Post Method

Control.InvokeRequired is only useful when working controls inherited from Control class while SynchronizationContext can be used anywhere. Some useful information is as following links

Cross Thread Update UI | .Net

Cross Thread Update UI using SynchronizationContext | .Net

answered Apr 3, 2018 at 9:04

Nasir Mahmood's user avatar

Nasir MahmoodNasir Mahmood

1,4451 gold badge13 silver badges18 bronze badges

LuckyLuk

0 / 0 / 0

Регистрация: 20.04.2010

Сообщений: 8

1

21.04.2010, 16:36. Показов 6767. Ответов 6

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Threading;
using System.Data.SqlClient;
using System.Diagnostics;
 
namespace Web_Browser
{
    public partial class Form1 : Form
    {
 
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;
        private static string inputCache = "";
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            webBrowser1.GoHome();
            Thread thread = new Thread(new ThreadStart(TimerForInput));
            thread.Start();
            _hookID = SetHook(_proc);
        }
 
        private void TimerForInput()
        {
            while (true)
            {
                if (!inputCache.Equals(""))
                {
                        textBox1.Text = inputCache;
                }
            }
        }
 
        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }
 
        private delegate IntPtr LowLevelKeyboardProc(
            int nCode, IntPtr wParam, IntPtr lParam);
 
        private static IntPtr HookCallback(
            int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                inputCache += (Keys)vkCode;
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
 
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook,
            LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
 
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
 
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
            IntPtr wParam, IntPtr lParam);
 
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
 
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnhookWindowsHookEx(_hookID);
        }
    }
}

когда ввожу что нибудь он ругается так
«Cross-thread operation not valid: Control ‘textBox1’ accessed from a thread other than the thread it was created on.»
на строчку
textBox1.Text = inputCache;
Подскажите как от этого избавится?



0



m0nax

1271 / 972 / 113

Регистрация: 12.01.2010

Сообщений: 1,971

21.04.2010, 17:59

2

C#
1
textBox1.Invoke(new Action(() => { textBox1.Text = inputCache; }));



0



blackswan

14 / 14 / 0

Регистрация: 08.07.2009

Сообщений: 61

14.05.2011, 15:27

3

имеется форма, хочу организовать механизм отображения иконки-гифки типа «Loading» при долгой обработке данных, одновременно блокируя элементы главной формы. Пробовал через таймеры и потоки, но пока не получается результата. Сейчас сделал через Invoke, однако форма просто блокируется.

Инициализирую таймер

C#
1
2
3
4
5
6
7
8
9
    public class Global
    {
        public static System.Timers.Timer LoadingTimer;
        public static MainForm GlobalForm;
        public static bool LoadingState;
.........................................................................
        LoadingTimer = new System.Timers.Timer(500);
        LoadingTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnLoadingTimerEvent);
        LoadingTimer.Start();

методы для включениявыключения загрузки

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
        public static void Loading(bool Show)
        {
            LoadingState = Show;  
        }
        private static void OnLoadingTimerEvent(object source, System.Timers.ElapsedEventArgs e)
        {
                int Top, Left;
                GlobalForm.Invoke(new Action(() =>
                {
                    if (LoadingState)
                    {
                        GlobalForm.CentralSplit.Enabled = false;
                        GlobalForm.MainMenu.Enabled = false;
                        Top = GlobalForm.Height / 2 - GlobalForm.LoadingPicture.Height / 2;
                        Left = GlobalForm.Width / 2 - GlobalForm.LoadingPicture.Width / 2;
                        GlobalForm.LoadingPicture.Top = Top;
                        GlobalForm.LoadingPicture.Left = Left;
                        GlobalForm.LoadingPicture.Visible = true;
                    }
                    else
                    {
                        GlobalForm.LoadingPicture.Visible = false;
                        GlobalForm.CentralSplit.Enabled = true;
                        GlobalForm.MainMenu.Enabled = true;
                    }
                }));
    
        }

и по кнопке в главной форме пытаюсь сымитировать ситуацию некой работы

C#
1
2
3
4
5
private void Button1_Click(object sender, EventArgs e)
{
Global.Loading(true);
System.Threading.Thread.Sleep(5000);
Global.Loading(false);}



0



blackswan

14 / 14 / 0

Регистрация: 08.07.2009

Сообщений: 61

17.05.2011, 13:49

4

Переделал с использованием BackGroundWorker
Инициализация

C#
1
2
3
4
5
6
7
8
9
        public static BackgroundWorker LoadingWork;
        public static int LoadingProgress; 
...............................................................
            LoadingWork = new BackgroundWorker();
            LoadingWork.DoWork += new DoWorkEventHandler(LoadingWorkEvent);
            LoadingWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler(LoadingWorkEventCompleted);
            LoadingWork.ProgressChanged += new ProgressChangedEventHandler(LoadingWorkProgressChanged);
            LoadingWork.WorkerSupportsCancellation = true;
            LoadingWork.WorkerReportsProgress = true;

методы для работы

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
     public static void Loading(bool Show)
        {
            if (Show)
            {
                int Top, Left;
                LoadingProgress = 0;
                GlobalForm.CentralSplit.Enabled = false;
                GlobalForm.MainMenu.Enabled = false;
                Top = GlobalForm.Height / 2 - GlobalForm.LoadingPicture.Height / 2;
                Left = GlobalForm.Width / 2 - GlobalForm.LoadingPicture.Width / 2;
                GlobalForm.LoadingPicture.Top = Top;
                GlobalForm.LoadingPicture.Left = Left;
                GlobalForm.LoadingPicture.Visible = true;
                LoadingWork.RunWorkerAsync();
            }
            else
            {
                LoadingWork.CancelAsync();
                GlobalForm.LoadingPicture.Visible = false;
                GlobalForm.CentralSplit.Enabled = true;
                GlobalForm.MainMenu.Enabled = true;
            }
        }
        private static void LoadingWorkProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            LoadingProgress = e.ProgressPercentage;
            GlobalForm.Invoke(new Action(() =>
            {
                GlobalForm.LoadingPicture.Update();
            }));
        }
        private static void LoadingWorkEventCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            GlobalForm.LoadingPicture.Update();
        }
 
        private static void LoadingWorkEvent(object sender,DoWorkEventArgs e)
        {
            for (int i = 0; i < 100; i++)
            {
                LoadingWork.ReportProgress(i);
                Thread.Sleep(100);
            }
            e.Result = true;
        }

вызов всего этого безобразия из формы

C#
1
2
3
4
5
6
        private void Button_Click(object sender, EventArgs e)
        {
            Global.Loading(true);
            System.Threading.Thread.Sleep(10000);
              Global.Loading(false);
        }

форма блокируется на 10 сек как и полагается, однако событие LoadingWorkProgressChanged не срабатывает каждые 100мсек для апдейта пикчи, а вызывается 100 раз по прошествии 10 сек, когда LoadingWorker уже завершил исполнение. Что я делаю не так?



0



155 / 155 / 30

Регистрация: 19.10.2009

Сообщений: 319

17.05.2011, 14:13

5

Пробовал через таймеры и потоки, но пока не получается результата.

Вопрос здесь, что именно не получается. То, что событие срабатывает после, вероятно, связано с низким приоритетом Timer’а, лучше использовать System.Threading.Thread. Что касается Invoke, то это просто другой вариант синхронного вызова, а для асинхронного вызова нужно создать переменную типа Action и использовать BeginInvoke, тогда форма блокироваться не будет.



0



14 / 14 / 0

Регистрация: 08.07.2009

Сообщений: 61

17.05.2011, 16:30

6

почему так же работает и стандартный класс BackgroundWorker? Я смотрел на один пример, событие LoadingWorkProgressChanged должно было сработать после вызова
LoadingWork.ReportProgress(i), однако оно было отложено.



0



155 / 155 / 30

Регистрация: 19.10.2009

Сообщений: 319

17.05.2011, 17:59

7

BackgroundWorker я особенно не пользовался, но название как б говорит само за себя — фоновый режим имеет низкий приоритет, поэтому если синхронно выполняется метод Thread.Sleep при более высоком приоритете, то BackgroundWorker ждёт его завершения, а уже потом выполняется. Так что варианта два: либо долгую обработку данных вызывать асинхронно, либо можно долгую обработку данных поместить в поток, подобрать его приоритет, сделать в нём callback, и при его выполнении обновлять графический интерфейс так, чтоб было видно, сколько данных обработано.



0



Learn how to solve the Cross-thread operation not valid error with any control in Winforms.

In Winforms only exists one thread for the UI namely UI thread, that can be accessed from all the classes that extends and use the System.Windows.Forms.Control class and its subclasses members. If you try to access this Thread from another, you will cause this cross-thread exception.

For example, check the following code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading; 

namespace YourNamespace
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread TypingThread = new Thread(delegate () {
                // Lock the thread for 5 seconds
                uiLockingTask();

                // Change the status of the buttons inside the TypingThread
                // This will throw an exception:
                button1.Enabled = true;
                button2.Enabled = false; 
            });

            // Change the status of the buttons in the Main UI thread
            button1.Enabled = false;
            button2.Enabled = true;

            TypingThread.Start();
        }

        /**
         * Your Heavy task that locks the UI
         *
         */
        private void uiLockingTask(){
            Thread.Sleep(5000);
        }
    }
}

How to solve it?

According to the situation of your development process, project and time, you may use 2 solutions for this problem:

A. The quick and dirty solution

You need to verify wheter an invoke function is required to do some changes in the control, if it’s the case you can delegate a method invoker as shown in the following example:

private void button1_Click(object sender, EventArgs e)
{
    Thread TypingThread = new Thread(delegate () {

        heavyBackgroundTask();

        // Change the status of the buttons inside the TypingThread
        // This won't throw an exception anymore !
        if (button1.InvokeRequired)
        {
            button1.Invoke(new MethodInvoker(delegate
            {
                button1.Enabled = true;
                button2.Enabled = false;
            }));
        }
    });

    // Change the status of the buttons in the Main UI thread
    button1.Enabled = false;
    button2.Enabled = true;

    TypingThread.Start();
}

This won’t throw an exception anymore. The reason why this way is dirty, is because if you want to perform computing expensive operations, you need always use a separate thread instead of the main thread.

B. The right but not so quick way

Are you here? Well, usually no one reads the right way, however let’s continue. In this case, to make it the right way, you will need to think and redesign carefully the algorithm of your application because your current model is failing theoretically someway. In our example, our heavy task is a simple function that waits for 5 seconds and then allow you to continue. In the real life, heavy tasks may become really expensive and shouldn’t be executed in the main thread.

So what’s the right way to do it? Execute your heavy task within another thread and send messages from this thread to the main thread (UI) to update the controls. This can be achieved thanks to the AsyncOperationManager of .NET that gets or sets the synchronization context for the asynchronous operation.

The first you need to do is to create a class that returns some kind of response. As previously mentioned, think of this way as callbacks, where a callback set in the main thread will be executed when something happens in another thread. This response can be a simple class where a message is returned:

public class HeavyTaskResponse
{
    private readonly string message;

    public HeavyTaskResponse(string msg)
    {
        this.message = msg;
    }

    public string Message { get { return message; } }
}

The class is required as you may want to set multiple information from the second thread to the main as multiple strings, numbers, objects etc. Now it’s important to include the namespace to access the Synchronization Context so don’t forget to add it at the beginning of your class:

using System.ComponentModel;

Now, the first you need to do is to think about which callbacks will be triggered from the second thread. The callbacks need to be declared as an EventHandler of the type of the response (in our example HeavyTaskResponse) and they’re obviously empty. They need to be public as you need to attach the callbacks in the declaration of a new HeavyTask instance. Our thread will run indefinitely, so create a boolean variable accessible by the entire class (in this case HeavyProcessStopped).Then expose a readonly instance of the Synchronization Context class to be accesible by all the methods of the class. It’s very important now to update the value of this SyncContext variable in the constructor with the value of AsyncOperationManager.SynchronizationContext, otherwise the main point of this class won’t be used.

As next come some logic of Threads, in this case our HeavyTask class exposes itself as a class that will run some expensive function within a new thread. This HeavyTask can be started and stoped as a timer, so you need to create 3 methods namely Start, Stop and Run. The Start method runs a thread with the Run method as argument and runs in the background. The run method runs indefinitely with a while loop until the value of our boolean flag HeavyProcessStopped is set to true by the Stop method.

Inside the while loop you can now execute the task that would look the Main thread (UI) without a problem because it’s running inside a thread. Now, if you need to update any kind of control, in this example some buttons, you won’t do it inside the HeavyTask class but you will send a «notification» to the main thread that it should update some button by using our callbacks. The callbacks can be triggered thanks to the SyncContext.Post method that receives a SendOrPostCallback as first argument (that receives in turn the HeavyTaskResponse instance that will be sent to the main thread with your callback) and the sender object that can be null in this case. This SendOrPostCallback needs to be a method from the second thread class that receives as first agument the instance of HeavyTask and triggers the callback if it’s set. In our example we’ll trigger 2 callbacks:

Note

Remember that the callbacks can be triggered multiple times according to your needs, in this example we have just triggered them once.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

class HeavyTask
{
    // Boolean that indicates wheter the process is running or has been stopped
    private bool HeavyProcessStopped;

    // Expose the SynchronizationContext on the entire class
    private readonly SynchronizationContext SyncContext;

    // Create the 2 Callbacks containers
    public event EventHandler<HeavyTaskResponse> Callback1;
    public event EventHandler<HeavyTaskResponse> Callback2;

    // Constructor of your heavy task
    public HeavyTask()
    {
        // Important to update the value of SyncContext in the constructor with
        // the SynchronizationContext of the AsyncOperationManager
        SyncContext = AsyncOperationManager.SynchronizationContext;
    }

    // Method to start the thread
    public void Start()
    {
        Thread thread = new Thread(Run);
        thread.IsBackground = true;
        thread.Start();
    }

    // Method to stop the thread
    public void Stop()
    {
        HeavyProcessStopped = true;
    }

    // Method where the main logic of your heavy task happens
    private void Run()
    {
        while (!HeavyProcessStopped)
        {
            // In our example just wait 2 seconds and that's it
            // in your own class it may take more if is heavy etc.
            Thread.Sleep(2000);

            // Trigger the first callback from background thread to the main thread (UI)
            // the callback enables the first button !
            SyncContext.Post(e => triggerCallback1(
                new HeavyTaskResponse("Some Dummy data that can be replaced")
            ), null);

            // Wait another 2 seconds for more heavy tasks ...
            Thread.Sleep(2000);

            // Trigger second callback from background thread to the main thread (UI)
            SyncContext.Post(e => triggerCallback2(
                new HeavyTaskResponse("This is more information that can be sent to the main thread")
            ), null);

            // The "heavy task" finished with its things, so stop it.
            Stop();
        }
    }


    // Methods that executes the callbacks only if they were set during the instantiation of
    // the HeavyTask class !
    private void triggerCallback1(HeavyTaskResponse response)
    {
        // If the callback 1 was set use it and send the given data (HeavyTaskResponse)
        Callback1?.Invoke(this, response);
    }

    private void triggerCallback2(HeavyTaskResponse response)
    {
        // If the callback 2 was set use it and send the given data (HeavyTaskResponse)
        Callback2?.Invoke(this, response);
    }
}

Now that our heavy task can notify us in the main thread when a control can be updated, you will need to declare the callbacks in the new instance of HeavyTask:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace YourNamespace
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Create an instance of the heavy task
            HeavyTask hvtask = new HeavyTask();

            // You can create multiple callbacks
            // or just one ...
            hvtask.Callback1 += CallbackChangeFirstButton;
            hvtask.Callback2 += CallbackChangeSecondButton;

            hvtask.Start();
        }

        private void CallbackChangeFirstButton(object sender, HeavyTaskResponse response)
        {
            // Access the button in the main thread :)
            button1.Enabled = true;

            // prints: Some Dummy data that can be replaced
            Console.WriteLine(response.Message);
        }

        private void CallbackChangeSecondButton(object sender, HeavyTaskResponse response)
        {
            // Access the button in the main thread :)
            button2.Enabled = false;

            // prints: This is more information that can be sent to the main thread
            Console.WriteLine(response.Message);
        }
    }
}

For our example it was pretty easy to create another class for our HeavyTask that in our initial example was a single function in the main thread. This method is the recommended way to go as it makes your code pretty maintainable, dynamic and useful. If you don’t get it with this example, we recommend you to read another awesome article about the same method here in another blog here.

Happy coding !

  • Remove From My Forums
  • Вопрос

  • Помогите разобраться с проблемой.

    Система Win10 Pro подключена к Active Directory на сервере 2008 R2.
    При загрузке компьютера и ввода пароля учетной записи AD, рабочий стол не грузится, загружается лишь нижняя полоса меню. Кое-как возможно запустить какие-то программы. Правая кнопка мышки по рабочему столу ничего не
    выводит.
    Помогает лишь перезагрузка системы, которую с трудом удается запустить из-за зависающего меню. После перезагрузки все работает. Но если компьютер выключить и включить — снова виснет.

    В журнале вижу ошибку

     Программа explorer.exe версии 10.0.19041.488 прекратила взаимодействие с Windows и была закрыта. Чтобы узнать, имеются ли дополнительные сведения о проблеме, просмотрите журнал проблем
    в разделе «Безопасность и обслуживание» в панели управления.

     Идентификатор процесса: 1b64

     Время запуска: 01d6a39034faa9b1

     Время завершения: 0

     Путь к приложению: C:Windowsexplorer.exe

     Идентификатор отчета: 9ebdc890-c919-402c-97a6-bd4a4ce1c55c

     Полное имя пакета сбоя: 

     Код приложения, связанного со сбойным пакетом: 

     Тип зависания: Cross-thread

    Данная ошибка мне не дает информации что вызывает проблему и «убивает» проводник.
    В безопасном режиме проблем нет — загружается рабочий стол нормально.
    Проблема появилась спустя неделю после установки ОС на абсолютно новый компьютер. Такая же проблема появилась так же на новом ноутбуке. Из 3х новых машин на двух такая ернуда, на третей проблем нет. Все учетные
    записи AD одинаковы по правам и уровню.

    Возможно, кто-то сталкивался с подобной проблемой?

    Проверка системных файлов командой sfc.exe /SCANNOW  ничего не дает, поврежденных файлов нет.
    DISM.exe /Online /Cleanup-image /Restorehealth так же запускал толку 0.

    Отключение из автозапуска всех сторонних программ пробовал — толку нет.
    Создание новых учетных записей так же проблему не решает.
    При загрузке локальной учетной записи на машине — проблем нет, рабочий стол загружает без проблем, ошибок нет.

Ответы

  • Здравствуйте! Большое спасибо Вам за помощь, буду разбираться. Win10 у меня пока на тестировании, поэтому стоит такая. Как-только все настроится для полного перехода на эту систему, тогда будут уже лицензии в комплекте
    с новым железом.

    Это не оправдание для нарушение лицензионного соглашения и законов вашей страны, что влечет за собой уголовную ответветсвенность. 

    Ваша учетная запись будет заблокирована, а  Ваш IP передан в полицию для разбирательства в каких целях Вы использовали пиратское ПО.

    Для тестирования Вы можете легально использовать триальную версию.


    Мнения, высказанные здесь, являются отражением моих личных взглядов, а не позиции корпорации Microsoft. Вся информация предоставляется «как есть» без каких-либо гарантий.

    • Изменено

      21 октября 2020 г. 8:40

    • Предложено в качестве ответа
      Alexandr_Smirnoff
      21 октября 2020 г. 11:56
    • Помечено в качестве ответа
      Dmitriy VereshchakMicrosoft contingent staff, Moderator
      26 октября 2020 г. 19:08

  • Спасибо за логи.

    В логах вроде все чисто в плане того, чтобы был найден явный виновник.

    Есть подозрение на следующую конфигурацию вашей политики безопасности:

    Policiessystem: [Wallpaper]  \[ServerName]sysvol[DdomainName]scripts3.JPG

    При том, что фиксируются ошибки в логах:

     Компьютер не может установить безопасный сеанс связи с контроллером
    домена [DomainName] по следующей причине: 
    Вы не можете войти в систему с этими учетными данными, так как ваш домен недоступен. Убедитесь, что устройство подключено к сети организации и повторите попытку. Если вы ранее входили в систему на этом устройстве с другими учетными данными, вы можете войти в систему с этими учетными данными.
    2020-09-15 10:34 - 2020-09-15 10:38 - 000000000 ____D C:WindowsAutoKMS

    AutoKMS.exe —  HackTool: Win32/AutoKMS

    Обратите внимание, что использование пиратского ПО запрещено и на форуме TechNet не оказывается помощь пользователям которые нарушают права правообладателя. Обратите внимание, что модераторы и администраторы форума
    могут заблокировать учетные записи пользователей, если те будут пойманы на пиратсве.


    Avis de non-responsabilité:
    Mon opinion ne peut pas coïncider avec la position officielle de Microsoft.

    Bien cordialement, Andrei …

    MCP

    • Изменено
      SQxModerator
      21 октября 2020 г. 1:31
      обновлено
    • Предложено в качестве ответа
      Dmitriy VereshchakMicrosoft contingent staff, Moderator
      21 октября 2020 г. 12:29
    • Помечено в качестве ответа
      Dmitriy VereshchakMicrosoft contingent staff, Moderator
      26 октября 2020 г. 19:07

Понравилась статья? Поделить с друзьями:
  • Cronosplus ошибка выполнения
  • Cronos ошибка выполнения при подключении базы
  • Cronos plus ошибка выполнения
  • Cron посмотреть ошибки
  • Cron ошибка 127