140

UART를 사용하여 마이크로 컨트롤러에서 온도 값을 C #인터페이스로 보내고 온도를 표시하고 싶습니다.Label.Content. 여기 내 마이크로 컨트롤러 코드 :

while(1) {
   key_scan(); // get value of temp
   if (Usart_Data_Ready())
   {
      while(temperature[i]!=0)
      {
         if(temperature[i]!=' ')
         {
            Usart_Write(temperature[i]);
            Delay_ms(1000);
         }
         i = i + 1;
      }
      i =0;
      Delay_ms(2000);
   }
}

내 C #코드는 다음과 같습니다.

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
   txt += serialPort1.ReadExisting().ToString();
   textBox1.Text = txt.ToString();
}

그러나 예외는 거기에서 일어난다 "크로스 스레드 작업이 유효하지 않습니다 : 'textBox1'이 (가) 생성 된 스레드가 아닌 다른 스레드에서 액세스 제어" 제 마이크로 컨트롤러에서 온도 스트링을 얻는 방법을 알려주고이 에러를 제거하십시오!


  • 다른 스레드를 통해 UI (메인 스레드)를 수정하려고합니다. - Evan Mulawski

6 답변


254

받은 데이터serialPort1_DataReceived메서드가 UI 스레드가 아닌 다른 스레드 컨텍스트에서오고 있기 때문에이 오류가 표시됩니다.

이 문제를 해결하려면 MSDN 문서에서 설명한대로 디스패처를 사용해야합니다.

방법 : Windows Forms 컨트롤에 스레드 안전 호출 만들기

따라서 텍스트 속성을 직접serialport1_DataReceived메소드에서이 패턴을 사용하십시오.

delegate void SetTextCallback(string text);

private void SetText(string text)
{
  // InvokeRequired required compares the thread ID of the
  // calling thread to the thread ID of the creating thread.
  // If these threads are different, it returns true.
  if (this.textBox1.InvokeRequired)
  { 
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
  }
  else
  {
    this.textBox1.Text = text;
  }
}

따라서 귀하의 경우 :

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
  txt += serialPort1.ReadExisting().ToString();
  SetText(txt.ToString());
}


  • 컨트롤에 InvokeRequired 속성이 없으면 문제가 발생합니다. 부모 폼의 InvokeRequired 속성을 사용해보십시오. 용도if (this.InvokeRequired) { //SetTextCallBack etc. }대신에if (this.textBox1.InvokeRequired) { //SetTextCallBack etc. } - Jroonk
  • 의지control.BeginInvoke너무 일해? 해결책은 이것 같이 1 개의 선 너무 일 수있다, 맞은?textbox1.BeginInvoke((MethodInvoker)delegate(){ textbox1.Text = txt.ToString(); }); - newbieguy
  • 만약 누군가가 이것을 놓친다면 (누가 나보다 Funcs와 lambdas에 더 익숙하다.)SetTextCallback호출하기 위해 노력한다.SetText네가 지나가는거야.SetTextnew SetTextCallback(). DUHHH - ErikE

42

이게 충분히 좋은지 모르겠지만 정적 ThreadHelperClass 클래스를 만들고 다음과 같이 구현했습니다. 이제 많은 코딩없이 다양한 컨트롤의 텍스트 속성을 쉽게 설정할 수 있습니다.

public static class ThreadHelperClass
    {
        delegate void SetTextCallback(Form f, Control ctrl, string text);
        /// <summary>
        /// Set text property of various controls
        /// </summary>
        /// <param name="form">The calling form</param>
        /// <param name="ctrl"></param>
        /// <param name="text"></param>
        public static void SetText(Form form, Control ctrl, string text)
        {
            // InvokeRequired required compares the thread ID of the 
            // calling thread to the thread ID of the creating thread. 
            // If these threads are different, it returns true. 
            if (ctrl.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                form.Invoke(d, new object[] { form, ctrl, text });
            }
            else
            {
                ctrl.Text = text;
            }
        }
    }

코드 사용 :

 private void btnTestThread_Click(object sender, EventArgs e)
        {
            Thread demoThread =
               new Thread(new ThreadStart(this.ThreadProcSafe));
            demoThread.Start();
        }

        // This method is executed on the worker thread and makes 
        // a thread-safe call on the TextBox control. 
        private void ThreadProcSafe()
        {
            ThreadHelperClass.SetText(this, textBox1, "This text was set safely.");
            ThreadHelperClass.SetText(this, textBox2, "another text was set safely.");
        }


  • That's awesome solution, 가장 멋진 것은"it's open for extension and generic". 원하는대로 새 UI 업데이트 기능을 추가 할 수 있습니다. 감사합니다. - Basheer AL-MOMANI
  • 좋은 물건! 그리고 대신 텍스트를 읽을 필요가 있다면 : GetTextCallback (Form f, Control ctrl); 공용 정적 문자열 GetText (양식 양식, Ctrl Ctrl) {문자열 텍스트; if (ctrl.InvokeRequired) {GetTextCallback d = 새 GetTextCallback (GetText); 텍스트 = (문자열) (form.Invoke (d, 새 개체 [] {양식, Ctrl 키))); } else {text = ctrl.Text; } return text; }} - hypers
  • ThreadProcSafe에서 내 맞춤 텍스트를 어떻게 사용하는지 설명 할 수 있습니까? Eliseo의 제안도 시도했지만 효과가 없습니다. - Pablo Costa

24

당신은 단순히 이것을 할 수 있습니다.

TextBox.CheckForIllegalCrossThreadCalls = false;


  • & #39 '을 할 때 오류라는 잘못된 생각이 다시 나타납니다. 엮다. - omglolbah
  • 그러나 멀티 스레드 프로그래밍을 교육하기위한 좋은 아이디어 - Mehdi Khademloo
  • 닷넷 2 시대에 작동했던 오래된 샘플 코드가 있습니다. 코드의 다른 측면을 분석하면됩니다. 따라서이 답변은 제가 한 일에 아주 좋습니다! - Dave
  • @DerfSkren 더 자세한 설명을 주실까요? ^ - Eric Wu
  • @EricWu는 플래그를 설정하면 디버깅을 수행하든 빌드를 릴리스하든 상관 없습니다. "문제" 실제로 안전하고 당신이 만든 모든 GUI에서 휠을 재발 명하도록 강요당하는 것에 대해 경고받는 것은 다시 돌아 오지 않습니다. - Derf Skren

20

다음 확장명을 사용하고 다음과 같은 동작을 전달하십시오.

_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));

확장 클래스 :

public static class CrossThreadExtensions
{
    public static void PerformSafely(this Control target, Action action)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action);
        }
        else
        {
            action();
        }
    }

    public static void PerformSafely<T1>(this Control target, Action<T1> action,T1 parameter)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, parameter);
        }
        else
        {
            action(parameter);
        }
    }

    public static void PerformSafely<T1,T2>(this Control target, Action<T1,T2> action, T1 p1,T2 p2)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, p1,p2);
        }
        else
        {
            action(p1,p2);
        }
    }
}


  • 이것은 훌륭하게 작동했습니다! 한가지주의 할게, 나는 목표물을 교체했다. 목표물로 호출하라 .BeginInvoke. 나는 교수형에 처한 과제에 대해 몇 가지 문제를 겪고 있었고 이것으로 그들 중 하나를 해결했다. - Edyn

10

이전 답변과 동일한 줄에 따라, 매우 짧은 추가로 크로스 스레드 인보 케이션 예외없이 모든 컨트롤 속성을 사용할 수 있습니다.

도우미 방법

    /// <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;
    }

샘플 사용법

    // 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;
    }


6

공유 컨테이너를 사용하여 스레드간에 데이터를 전송하십시오.

연결된 질문


관련된 질문

최근 질문