【问题标题】:A Question About C# Delegates关于 C# 代表的问题
【发布时间】:2011-06-10 17:01:31
【问题描述】:
class ClassA
{
public delegate void WriteLog(string msg);
private WriteLog m_WriteLogDelegate;

public ClassA(WriteLog writelog)
{
    m_WriteLogDelegate =  writelog;
    Thread thread = new Thread(new ThreadStart(Search));
    thread.Start();
}

public void Search()
{
    /* ... */
    m_WriteLogDelegate("msg");
    /* ... */
}

}

class classB
{
        private ClassA m_classA;

        protected void WriteLogCallBack(string msg)
        {
            // prints msg
            /* ... */
        }

        public classB()
        {
            m_classA = new ClassA(new WriteLog(WriteLogCallBack));
        }

        public void test1()
        {
            Thread thread = new Thread(new ThreadStart(Run));
            thread.Start();
        }

        public void test2()
        {
            m_classA.Search();
        }

        public void Run()
        {
            while(true)
            {
                /* ... */
                m_classA.Search();
                /* ... */
                Thread.Sleep(1000);
            }
        }
}

为什么是下面的代码

ClassB b = new ClassB();
b.test2() 

打印“味精” 还有这个

ClassB b = new ClassB();
b.test1() 

什么都不打印?

【问题讨论】:

  • 我在test1()看到一个帖子...
  • test2 的代码在哪里?以及传递给 Thread 构造函数的 Run 方法的代码?
  • 在代码中向下滚动;它就在那里。
  • ClassA 的构造函数是什么样的?

标签: c# multithreading delegates


【解决方案1】:

您的程序可能退出导致线程终止(或在线程有时间启动之前)。 正如你显式创建了一个线程,你需要显式地等待线程完成!

您需要使用Thread.Join 或其他方法让主程序等待线程完成。

一种可能的选择,使用Thread.Join

public Thread test2()
{
    ...
    return thread;
}

...

b.test2().Join(); // wait for test2 to complete

另一种选择,使用ManualResetEvent

class classB
{
    private ManualResetEvent mre = new ManualResetEvent(false);

    ...

    private void Run()
    {
        ...

        this.mre.Set(); // we completed our task
    }

    public void Wait();
    {
        this.mre.WaitOne();
    }

然后你的代码调用b.test2():

b.test2();
b.Wait();

【讨论】:

  • 这很奇怪——当我在没有Join() 的情况下运行程序时,打印线程继续运行并且程序永远不会终止。我本来希望操作系统至少切换到主线程一次,导致程序终止。
  • @Steve:这取决于程序是如何运行控制台/GUI的。
  • 我忘了提到ClassA.Search 也在一个单独的线程中运行。会不会是这个原因?
  • @LAS_VEGAS,我会遵从,“就像你明确地创建了一个线程一样,你需要明确地等待线程完成”。您需要确保您的程序在其所有线程的持续时间内都存在,包括从线程产生的线程。
【解决方案2】:

您的代码对我有用,尽管我必须充实帖子中省略的部分。除非我做的事情与你所做的完全不同,否则问题一定出在其他地方。

以下代码在控制台应用程序中运行良好:我看到每隔 1 秒打印一次“测试”。

internal class ClassA
{
    public WriteLog Log { get; set; }

    public ClassA(WriteLog writeLog)
    {
        Log = writeLog;
    }

    public void Search()
    {
        Log.Print("Test");
    }
}

class classB
{
    private ClassA m_classA;

    protected void WriteLogCallBack(string msg)
    {
        // prints msg
        /* ... */
        Console.WriteLine(msg);
    }

    public classB()
    {
        m_classA = new ClassA(new WriteLog(WriteLogCallBack));
    }



    public void test1()
    {
        Thread thread = new Thread(new ThreadStart(Run));
        thread.Start();
    }

    public void test2()
    {
        m_classA.Search();
    }

    public void Run()
    {
        while (true)
        {
            /* ... */
            m_classA.Search();
            /* ... */
            Thread.Sleep(1000);
        }
    }
}

internal class WriteLog
{
    private Action<string> Callback { get; set; }

    public WriteLog(Action<string> writeLogCallBack)
    {
        Callback = writeLogCallBack;
    }

    public void Print(string msg)
    {
        Callback(msg);
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        classB b = new classB();
        b.test1();
        }
}

【讨论】:

  • 我忘了提到ClassA.Search 也在单独的线程中运行。会不会是这个原因?
  • @LAS 不确定。如果您发布 Search() 的实现可能会有所帮助。
【解决方案3】:

在什么情况下调用 b.test1()?如果它是一个控制台应用程序并且调用 b.test1() 之后的下一件事是终止程序,那么由 b.test1() 创建的线程可能永远不会在程序终止之前执行。

您需要等待以留出足够的时间来构建(昂贵)并安排执行新线程。 “多线程”和“并发”并不意味着瞬时。它们意味着每单位时间的工作量平均超过大量工作量。

要降低后台线程操作的成本,请考虑将您的new Thread() 替换为ThreadPool.QueueUserWorkItem() 以利用现有的工作池线程。这将节省时间和内存。

另外,请仔细考虑您正在推进到后台线程的工作是否真的值得额外的线程开销。如果写入日志可能会阻塞文件 I/O 或网络 I/O,那么在后台线程中执行此操作可能会受到警告,但也有其他技术可以执行异步 I/O,而无需创建新的线程。

还要考虑频率。最好启动一个后台线程来侦听队列并大部分时间都在休眠,并让主线程将消息推送到队列中,而不是每次想要输出几个字符时启动一个新线程文本。

【讨论】:

    猜你喜欢
    • 2010-10-20
    • 1970-01-01
    • 2022-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    相关资源
    最近更新 更多