如何在后台工作程序中执行应用程序 运行 期间更新进度条

How to get progress bar to update DURING execution of an app running in a backgroundworker

我找到了很多关于Backgroundworker更新进度条的资料,我也写过很多版本的代码。但是 none 个版本在我的升级申请 运行ning 期间更新了进度条。这是我使用过的 DoWork 处理程序版本之一:

 void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        updater.updater();

        int percents = 0;

        // Progress bar
        int total = 57;

        for (int i = 0; i <= total; i++)
        {
            System.Threading.Thread.Sleep(100);
            percents = (i * 100) / total;
            bw.ReportProgress(percents, i);
        }

如果我在 ReportProgress 之前 运行 updater(我的应用程序)(如图所示),updater 运行s 完全然后进度条从 0 更新到 100%。如果我在 ReportProgress 调用之后放置更新程序,进度条 运行s 和然后更新程序 运行s。如果我用更新程序替换 Thread.Sleep 行,它会 运行 在进度条的 0% 间隔处。

在 backgroundworker 中执行一个长时间 运行ning 应用程序时,是否真的可以更新进度条? 这就是 backgroundworker 的 MSDN 页面声称,但它们实际显示的是 运行 一系列短流程(睡眠)而不是一个长流程。我在网上找到的大多数示例都使用这种格式,没有引用未分段到 ReportProgress 部分的较长 运行ning 过程。

我很想知道 backgroundworker 是否能够做到这一点,或者这是其他线程类型解决方案的工作。

谢谢!

在看到下面 Tim 的回答后,我尝试为进度条进度实现一个 EventArg 和 Handler。

public class FWupdater
{
    public string comPort;
    public int percentage;
    public State state;
    public string path;
    public const int ACK = 0x79;
    public const int NACK = 0x1F;

    public class PBProgressEventArgs : EventArgs
    {
        private int prog;

        public int progress
        {
            set { prog = value; }
            get { return this.prog; }
        }
    }

    public class PBProgress
    {
        public event PBProgressHandler Progress;
        public delegate void PBProgressHandler(PBProgress p, PBProgressEventArgs e);
        public void Start()
        {
            if (Progress != null)
            {
                PBProgressEventArgs progressUpdate = new PBProgressEventArgs();
                progressUpdate.progress = 0;
                Progress(this, progressUpdate);
            }
        }
    }

然后在主程序中创建一个实例,让后台可以看到。

PBProgress progUpdater = new PBProgress();

但是我无法让后台工作人员从 DoWork 方法中查看进度百分比。

包括更新程序代码。

public void updater()
    {
        // Create a new SerialPort object.
        SerialPort _serialPort;
        _serialPort = new SerialPort(comPort, 115200, Parity.Even, 8, StopBits.One);

        // for state machine
        bool _continue = true;

        try
        {
            _serialPort.Open();

            if (_serialPort.IsOpen)
            {
                Console.WriteLine("");
                Console.WriteLine("Serial Port is Open");
                Console.WriteLine("");
            }
            else
            {
                MessageBox.Show("Serial Port is not open. Choose another port.");
            }
        }
        catch (UnauthorizedAccessException ex)
        {
            MessageBox.Show(ex.Message);
        }
        catch (ArgumentOutOfRangeException ex)
        {
            MessageBox.Show(ex.Message);
        }
        catch (ArgumentException ex)
        {
            MessageBox.Show(ex.Message);
        }
        catch (IOException ex)
        {
            MessageBox.Show(ex.Message);
        }
        catch (InvalidOperationException ex)
        {
            MessageBox.Show(ex.Message);
        }


        // Move through states until upgrade is complete
        while (_continue)
        {
            switch (state)
            {
                case State.NORMAL:

                    // Beginning state for instance of upgrader

                    break;

                case State.WAITING_TO_UPGRADE:

                    SetUpComm( _serialPort);
                    state = State.ERASING_FIRMWARE;
                    break;

                case State.ERASING_FIRMWARE:

                    EraseFlashMemory(_serialPort);
                    state = State.UPGRADING_FIRMWARE;
                    break;

                case State.UPGRADING_FIRMWARE:

                    WriteNewAppToFlash(_serialPort);
                    state = State.UPGRADE_COMPLETE;
                    break;

                case State.UPGRADE_COMPLETE:

                    JumpToNewApp(_serialPort);
                    _continue = false;
                    _serialPort.Close();
                    break;

                default:
                    break;

            } // end SWITCH (state)

        } // end WHILE (_continue) - main loop

    } // end public void updater()
      // 

    // ---- METHODS -------------------



    public void SetUpComm(SerialPort _serialPort)
    {
        int byte_read = 0x00;
        var sevenF = new byte[] { 0x7F };

        // Send 0x55 and 0xAA to peripheral input to execute SwitchToBootloader()
        var byte1 = new byte[] { 0x55 };
        var byte2 = new byte[] { 0xAA };

        _serialPort.Write(byte1, 0, 1);
        _serialPort.Write(byte2, 0, 1);

        // If in bootloader mode, where the boot pins on the board are set,
        // the device will be looking to receive 0x7F to establish contact with the host.
        // In this case, the bytes to trigger boot load from inside the firmware will be
        // ignored and the following 0x7F will serve to trigger comm set-up .

        // Wait for acknowledge byte from USART
        while (byte_read != ACK)
        {
            // Write "7F" to start communicating with Bootloader
            _serialPort.Write(sevenF, 0, 1);
            Thread.Sleep(100);

            // read ACK byte after parameters set and bootloader running
            byte_read = _serialPort.ReadByte();
        }
    }


    public void EraseFlashMemory(SerialPort _serialPort)
    {
        int byte_read = 0;
        var ff = new byte[] { 0xFF };



        Console.WriteLine("Erasing flash memory...");
        Console.WriteLine("");

        /* NOTE: the ERASE COMMAND is not supported by this device, use EXTENDED ERASE */

        // Send 0x44 and 0xBB (extended erase memory command), see AN3155
        var exeraseMem = new byte[] { 0x44 };
        var bb = new byte[] { 0xBB };

        _serialPort.Write(exeraseMem, 0, 1);
        _serialPort.Write(bb, 0, 1);

        // Receive ACK byte
        byte_read = _serialPort.ReadByte();

        if (byte_read == NACK)
        {
            //Console.WriteLine("NACK received for ERASE MEMORY start");
            //Console.WriteLine("");
        }
        ////  end sending EXTENDED ERASE COMMAND

        //---------------------------------------
        // Global erase (send 0xFFFF, and 0x00)
        //---------------------------------------
        //var globalErase = new byte[] { 0x00 };
        //_serialPort.Write(ff, 0, 1);
        //_serialPort.Write(ff, 0, 1);
        //_serialPort.Write(globalErase, 0, 1);

        // Erase all but the first page (16k)
        // send number of pages to erase, msb first [11 pages, leaving page 0]
        // *ALERT* send 10 pages (N) to erase 11, for some reason it erases N + 1, whatever...
        var num_pages_msb = new byte[] { 0x00 };
        var num_pages_lsb = new byte[] { 0x0A };
        _serialPort.Write(num_pages_msb, 0, 1);
        _serialPort.Write(num_pages_lsb, 0, 1);

        // send page numbers, 2 bytes each, msb first

        // PAGE 1
        var page01_msb = new byte[] { 0x00 }; 
        var page01_lsb = new byte[] { 0x01 }; 
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page01_lsb, 0, 1);  // 1

        // PAGE 2
        var page02_lsb = new byte[] { 0x02 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page02_lsb, 0, 1);  // 2

        // PAGE 3
        var page03_lsb = new byte[] { 0x03 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page03_lsb, 0, 1);  // 3

        // PAGE 4
        var page04_lsb = new byte[] { 0x04 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page04_lsb, 0, 1);  // 4

        // PAGE 5
        var page05_lsb = new byte[] { 0x05 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page05_lsb, 0, 1);  // 5

        // PAGE 6
        var page06_lsb = new byte[] { 0x06 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page06_lsb, 0, 1);  // 6

        // PAGE 7
        var page07_lsb = new byte[] { 0x07 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page07_lsb, 0, 1);  // 7

        // PAGE 8
        var page08_lsb = new byte[] { 0x08 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page08_lsb, 0, 1);  // 8

        // PAGE 9
        var page09_lsb = new byte[] { 0x09 };
        _serialPort.Write(page01_msb, 0, 1);  // 0
        _serialPort.Write(page09_lsb, 0, 1);  // 9

        // PAGE 10
        var page10_msb = new byte[] { 0x01 };  // 1
        var page10_lsb = new byte[] { 0x00 };  // 0
        _serialPort.Write(page10_msb, 0, 1);
        _serialPort.Write(page10_lsb, 0, 1);

        // PAGE 11
        _serialPort.Write(page10_msb, 0, 1);  // 1
        _serialPort.Write(page01_lsb, 0, 1);  // 1

        // checksum = A
        _serialPort.Write(num_pages_lsb, 0, 1);

        // Receive ACK byte
        byte_read = _serialPort.ReadByte();


        bw.ReportProgress(20);

        if (byte_read == NACK)
        {
            //Console.WriteLine("NACK received for ERASE MEMORY completed");
            //Console.WriteLine("");
        }
    }
    // -- end EXTENDED ERASE MEMORY --------------------------------------------------


    public void WriteNewAppToFlash(SerialPort _serialPort)
    {
        // For testing
        int blockCount = 0;

        int byte_read = 0;
        long checksum = 0;
        var ff = new byte[] { 0xFF };

        // ------------------------------------------------------------------------------
        // -------- WRITE MEMORY --------------------------------------------------------
        // ------------------------------------------------------------------------------

        // for Address
        int baseAddress = 0x08008000;
        int offset = 0;

        // for string from HEX file
        string line;
        string[] lineBuffer = new string[16];
        int lineCount = 0;
        int length;
        int type;
        int hexChecksum = 0;

        bool sendAddress = true;

        int counter = 0;            // Counting the number of lines in the file
        int byteCounter = 0;        // Counting nmumber of bytes in the current block

        // Create byte array with 256 bytes
        byte[] buffer256 = new byte[256];

        // Read the file and process one line at a time
        System.IO.StreamReader file = new System.IO.StreamReader(path);
        while ((line = file.ReadLine()) != null)
        {
            // Store line into a line buffer. This will allow reprocessing of all lines 
            // in a block if there is an error sending a block of 256 bytes below
            if( line[8] == '0')
            {
                lineBuffer[lineCount++] = line;
            }

            // Send  WRITE COMMAND and the next address every 256 bytes
            if (sendAddress == true)
            {
                /* 
    -------------------------------------------------------------------------------------------------------
                    SEND WRITE COMMAND
    -----------------------------------------------------------------------------------------------------*/

                do
                {
                    // Send WRITE command - 0x31 and 0xCE
                    var writeMem = new byte[] { 0x31 };
                    var ce = new byte[] { 0xCE };

                    _serialPort.Write(writeMem, 0, 1);
                    _serialPort.Write(ce, 0, 1);

                    // Receive ACK byte
                    byte_read = _serialPort.ReadByte();

                } while (byte_read != ACK);

                // -- end SEND 0x31 and 0xCE and wait for ACK -----------------------------------------

                /* 
    -------------------------------------------------------------------------------------------------------
                SEND CURRENT ADDRESS AND CHECKSUM TO FLASH MEMORY
    -----------------------------------------------------------------------------------------------------*/

                Byte[] currentAddr = BitConverter.GetBytes(baseAddress + offset);

                // Increment offset by 0x100 (256 bytes)
                offset = offset + 0x00000100;

                // Reset Checksum and XOR address
                checksum = 0;
                foreach (byte b in currentAddr)
                {
                    checksum ^= b;
                }

                Byte[] cksum = BitConverter.GetBytes(checksum);

                // Send address, MSB first, LSB last
                _serialPort.Write(currentAddr, 3, 1);
                _serialPort.Write(currentAddr, 2, 1);
                _serialPort.Write(currentAddr, 1, 1);
                _serialPort.Write(currentAddr, 0, 1);

                // Send checksum of address bytes
                _serialPort.Write(cksum, 0, 1);

                // Receive ACK byte
                byte_read = _serialPort.ReadByte();

                if (byte_read == NACK)
                {
                    // Handle 
                }
                // -- end addr or increment --------------------------------------------------------- 

                sendAddress = false;

                // Send number of bytes, always 256, the last group will be padded with 0xFF
                _serialPort.Write(ff, 0, 1);

            } // end IF for WRITE COMMAND and ADDRESS


            /* FIRST CHARACTER in HEX FILE
               The colon indicates the start of a "record"
               Remove colon from beginning of string                                             */
            line = line.Substring(1, line.Length - 1);

            // Create byte array from string for whole line from HEX file
            var bytes = GetBytesFromByteString(line).ToArray();

            // Identify RECORD TYPE of HEX line [byte 4]
            type = bytes[3];

            /* Next TWO CHARACTERS   00-data                       03-start segment address
               in HEX FILE are       01-EOF                        04-extended linear address
               the record type:      02-extended segment address   05-start linear address      */

            // BLOCK WRITE TO MEMORY
            if (type == 0)
            {
                // Length of line is stored at byte 0, in this case 0x10, or 16 bytes of data
                length = bytes[0];

                // Add data from current line to buffer of 256 bytes
                for (int i = 0; i < length; i++)
                {
                    // Stuff all bytes from line into buffer of 256 bytes
                    buffer256[byteCounter++] = bytes[4 + i];

                    // Add byte to checksum
                    hexChecksum ^= bytes[4 + i];
                }

                // When buffer is full, send block of 256 bytes and checksum, reset variables for next block
                if (byteCounter >= 255)
                {

                    // Convert checksum to a byte value
                    hexChecksum = hexChecksum ^ 0xFF;
                    byte csByte = Convert.ToByte(hexChecksum);
                    Byte[] csByte_arr = BitConverter.GetBytes(csByte);

                    // Send byte array
                    _serialPort.Write(buffer256, 0, 256);

                    // For testing
                    // Console.WriteLine("block number [{0}]", ++blockCount);

                    //send checksum
                    _serialPort.Write(csByte_arr, 0, 1);

                    //Receive ACK byte
                    byte_read = _serialPort.ReadByte();
                    Console.WriteLine("block/ACK = [{0}] | {1}", ++blockCount, byte_read);

                    while (byte_read != ACK)
                    {
                        Array.Clear(buffer256, 0, buffer256.Length);
                        hexChecksum = 0;
                        lineCount = 0;

                        // reprocess the previous 16 lines stored in the line buffer
                        for ( int j = 0; j < 16; j++ )
                        {
                            line = lineBuffer[j];

                            line = line.Substring(1, line.Length - 1);
                            var bytesLocal = GetBytesFromByteString(line).ToArray();

                            length = bytesLocal[0];
                            for (int i = 0; i < length; i++)
                            {
                                buffer256[byteCounter++] = bytesLocal[4 + i];
                                hexChecksum ^= bytesLocal[4 + i];
                            }
                        }

                        // Convert checksum to a byte value
                        hexChecksum = hexChecksum ^ 0xFF;
                        byte csByteLocal = Convert.ToByte(hexChecksum);
                        Byte[] csByte_arrLocal = BitConverter.GetBytes(csByteLocal);

                        // Send byte array
                        _serialPort.Write(buffer256, 0, 256);

                        //send checksum
                        _serialPort.Write(csByte_arrLocal, 0, 1);

                        //Receive ACK byte
                        byte_read = _serialPort.ReadByte();
                        Console.WriteLine("block/ACK = [{0}] | {1}", ++blockCount, byte_read);
                    }

                    // Clear buffer, reset byte count, clear checksum, set flag to send write cmd/send new addr
                    Array.Clear(buffer256, 0, buffer256.Length);
                    byteCounter = 0;
                    hexChecksum = 0;
                    lineCount = 0;
                    sendAddress = true;
                }

            }  // end BLOCK WRITE TO MEMORY

            else if (type == 1)  // Marker for end of file
            {
                while (byteCounter != 0)
                {
                    // Add 0xFF to the remaining bytes in this last block of 256
                    buffer256[byteCounter++] = 0xFF;

                    // Add byte to checksum
                    hexChecksum ^= 0xFF;

                    if (byteCounter >= 255)
                    {
                        byteCounter = 0;

                        // Convert checksum to a byte value
                        hexChecksum = hexChecksum ^ 0xFF;
                        byte csByte = Convert.ToByte(hexChecksum);
                        Byte[] csByte_arr = BitConverter.GetBytes(csByte);

                        // Send byte array
                        _serialPort.Write(buffer256, 0, 256);

                        // For testing
                        // Console.WriteLine("block number [{0}]", ++blockCount);

                        //send checksum
                        _serialPort.Write(csByte_arr, 0, 1);

                        //Receive ACK byte
                        byte_read = _serialPort.ReadByte();
                        Console.WriteLine("block/ACK = [{0}] | {1}", ++blockCount, byte_read);

                        if (byte_read == NACK)
                        {
                            // ?? 
                        }
                    }
                }
            }
            // end ELSE if TYPE == 1

            counter++;
        } // end WHILE loop for loading hex file

        file.Close();

        // For testing
        // Console.WriteLine("File is closed.");
        // System.Console.WriteLine("There were {0} lines.", counter);
        // Console.WriteLine("");

       // -- end WRITE MEMORY ------------------------------------------------------

    }  // end  WriteNewAppToFlash



    private void handleAppSerialError(IOException exc)
    {
        throw new NotImplementedException();
    }

    private void raiseAppSerialDataEvent(byte[] received)
    {
        throw new NotImplementedException();
    }

    public void JumpToNewApp(SerialPort _serialPort)
    {
        int byte_read = 0;
        long checksum = 0;
        var ff = new byte[] { 0xFF };
        int baseAddress = 0x08000000;


        // Jumps to flash memory 0x08000000, where the sector 0 code will perform a normal startup

        // Send 0x21 ( GO ) and complement 0xDE
        var go = new byte[] { 0x21 };
        var de = new byte[] { 0xDE };

        while (byte_read != 0x79)
        {
            _serialPort.Write(go, 0, 1);
            _serialPort.Write(de, 0, 1);

            // Receive ACK byte
            byte_read = _serialPort.ReadByte();

            if (byte_read == NACK)
            {
                //Console.WriteLine("NACK received for GO COMMAND start");
                //Console.WriteLine("");
            }
        }

        // -- end SEND GO COMMAND and wait for ACK -----------------------------------------


        Byte[] startAddr = BitConverter.GetBytes(baseAddress);

        // Reset Checksum and XOR address
        checksum = 0;
        foreach (byte b in startAddr)
        {
            checksum ^= b;
        }

        Byte[] cheksum = BitConverter.GetBytes(checksum);

        // Send first byte (msb) of address
        _serialPort.Write(startAddr, 3, 1);

        // Send second byte of address
        _serialPort.Write(startAddr, 2, 1);

        // Send third byte of address
        _serialPort.Write(startAddr, 1, 1);

        // Send last byte (lsb) of address
        _serialPort.Write(startAddr, 0, 1);

        _serialPort.Write(cheksum, 0, 1);

        Thread.Sleep(20);
        // Receive ACK byte
        byte_read = _serialPort.ReadByte();

    }  // end JUMPTONEWAPP


    // Converts a string to a byte array
    public static IEnumerable<byte> GetBytesFromByteString(string str)
    {
        for (int index = 0; index < str.Length; index += 2)
        {
            yield return Convert.ToByte(str.Substring(index, 2), 16);
        }
    }

    protected void AssertOpenPort()
    {
        //          if( !IsOpen )
        //              throw new InvalidOperationException("Serial Port is not open");
    }

} // end public class FWupdater

如果您正在寻找真正的进展,那么您的更新程序将需要在进展过程中提高进度。您可以从 updater 中引发事件,并从 worker_DoWork 中订阅它们,然后使用 ReportProgress 将其编组回 UI 线程以获取进度报告:

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    updater.Progress += updater_Progress;
    try {
        updater.updater();
    } finally {
       updater.Progress -= updater_Progress;
    }
}
void updater_Progress(object sender, ProgressEvents evt) {
    worker.ReportProgress(evt.Percent);
}

这当然需要您在 Updater class 中创建一个 Progress 事件,并在 updater 方法工作时调用该事件。

BackgroundWorker 为您做了两件事:

  • 让您 运行 在后台线程中执行任务,以便您的 UI 线程保持响应
  • 让您轻松地将后台线程的进度编组到 UI 线程,而无需使用 Form.Invoke

DoWork 事件在后台线程中触发。该事件处理程序中的所有内容都按顺序发生,就像普通代码一样——而您的 UI 线程愉快地继续运行。如果你想要假进度,你可以使用 UI 线程的定时器回调来更新进度,而 BackgroundWorker 运行 在后台

更新你的代码

你可以这样解决

BackgroundWorker worker;

public void Init()
{
    worker = new BackgroundWorker();
    worker.DoWork += Worker_DoWork;
    worker.ProgressChanged += Worker_ProgressChanged;
    worker.WorkerReportsProgress = true; // This is important
    worker.RunWorkerAsync();
}

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    // Do your update progress here...

    for (int i = 0; i <= 100; i++) // This simulates the update process
    {
        System.Threading.Thread.Sleep(100);
        worker.ReportProgress(i); // Report progress from the background worker like this
    }
}

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Update the progress bar or other ui elements here...
    // Use the e.ProgressPercentage
}

长时间运行后台工作者是完全可以的。我从来没有遇到过任何问题,即使一直有一个 运行ning。

问题是在升级期间获取更新,因此从执行您尝试测量的工作的程序中发送百分比变化是有意义的。我缺少的部分由@mjwills 提供 - 将 BackgroundWorker 作为参数传递给更新程序允许我从更新程序调用 ReportProgress 并根据需要增加百分比值。

我使用的 BackgroundWorker (bw) 设置与 MSDN 中显示的非常相似。以下是 bw 的方法,我将其放在我的表单 class 中。

        BackgroundWorker bw = new BackgroundWorker();

当客户端选择了 COM 端口和升级文件时,然后是按钮单击事件(显示方法结束),然后是 bw 方法。

            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

            bw.WorkerReportsProgress = true;

            bw.RunWorkerAsync();

            pbar.Maximum = 100;
            pbar.Minimum = 0;
            pbar.Value = 0;

            // Percentage will be added to the end of this line during the upgrade
            updateMsg.Content = "Upgrade in progress...     ";
        }
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bw = sender as BackgroundWorker;
        updater.updater(bw);
    }

    void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    { 
        pbar.Value = e.ProgressPercentage;
        updateMsg.Content = String.Format("Upgrade in progress...  {0} %", e.ProgressPercentage);
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        updateMsg.Content = "Upgrade Complete. Exit window to proceed...";
    }

在updater()端,定义一个百分比变量:

public int percentage;

添加 BackgroundWorker 作为参数:

public void updater( BackgroundWorker bw ) {  <code> }

然后调用 ReportProgress 更新 bw_ProgressChanged 方法中的 ProgressPercentage 事件。从 0% 开始并递增百分比变量:

bw.ReportProgress(percentage += 5); 

后来,我在将许多数据块写入闪存时将更新更改为单个百分比:

// update progress bar in backgroundWorker thread
   if ( blockCount % 10 == 0)
   {
       bw.ReportProgress(percentage++);
   }

我要感谢大家的投入,我希望这个答案可以节省编写额外一千行代码的人。我希望收到关于这个答案的反馈,我仍然对替代的、更好的解决方案感兴趣。