[源码解读]深入理解pthread中的condition条件变量

2023-11-01

深入理解pthread中的condition条件变量


1.下面情况适合用pthread_cond_broadcast

  • 一个生产者多消费者,生产者能一次产生多个产品的情况。
  • 多生产者多消费者
  • 读写锁实现(写入之后,通知所有读者)

2.下面情况适合pthread_cond_signal的情况

  • 单一生产者,生产者一次生产一个产品的情况,最好一个消费者

注意:pthread_cond_signal在单一异步唤醒的处理线程的情况时,是不安全的

pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() 或pthread_cond_broadcast来唤醒它 。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait() 函数一进入wait状态就会自动release mutex。当其他线程通过 pthread_cond_signal() 或pthread_cond_broadcast ,把该线程唤醒,使 pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex 。

pthread_cond_signal 函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。

使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。

但是 pthread_cond_signal 在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait。

下面主要从源码级别来讨论一下condition的初始化和唤醒操作

参考源码为 glibc2.17下载链接

pthread_cond_init.c

condition的初始化

int __pthread_cond_init (cond, cond_attr)
     pthread_cond_t *cond;
     const pthread_condattr_t *cond_attr;
{
  struct pthread_condattr *icond_attr = (struct pthread_condattr *) cond_attr;

  cond->__data.__lock = LLL_LOCK_INITIALIZER; /// 0
  cond->__data.__futex = 0;
  /// icond_attr->value的bit0标识是否进程间共享, Bit 1-7: clock ID
  cond->__data.__nwaiters = (icond_attr != NULL
			     ? ((icond_attr->value >> 1)
				& ((1 << COND_NWAITERS_SHIFT) - 1))
			     : CLOCK_REALTIME);  /// COND_NWAITERS_SHIFT 1, CLOCK_REALTIME 0
  cond->__data.__total_seq = 0;
  cond->__data.__wakeup_seq = 0;
  cond->__data.__woken_seq = 0;
  cond->__data.__mutex = (icond_attr == NULL || (icond_attr->value & 1) == 0
			  ? NULL : (void *) ~0l);
  cond->__data.__broadcast_seq = 0;

  LIBC_PROBE (cond_init, 2, cond, cond_attr);

  return 0;
}

pthreadtypes.h , 数据类型定义

typedef union
{
  char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];///__SIZEOF_PTHREAD_MUTEXATTR_T 4
  int __align;
} pthread_mutexattr_t;


/* Data structure for conditional variable handling.  The structure of
   the attribute type is deliberately not exposed.  */
typedef union
{
  struct
  {
    int __lock;
    unsigned int __futex;
    __extension__ unsigned long long int __total_seq;
    __extension__ unsigned long long int __wakeup_seq;
    __extension__ unsigned long long int __woken_seq;
    void *__mutex;
    unsigned int __nwaiters;
    unsigned int __broadcast_seq;
  } __data;
  char __size[__SIZEOF_PTHREAD_COND_T]; ///__SIZEOF_PTHREAD_COND_T 48
  __extension__ long long int __align;
} pthread_cond_t;

typedef union
{
  char __size[__SIZEOF_PTHREAD_CONDATTR_T]; /// __SIZEOF_PTHREAD_CONDATTR_T 4
  int __align;
} pthread_condattr_t;


/* Keys for thread-specific data */
typedef unsigned int pthread_key_t;


/* Once-only execution */
typedef int pthread_once_t;

/* Conditional variable attribute data structure.  */
struct pthread_condattr
{
  /* Combination of values:

     Bit 0  : flag whether coditional variable will be shareable between
	      processes.

     Bit 1-7: clock ID.  */
  int value;
};

我们经常看到条件变量的初始化:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
/* Conditional variable handling.  */
#define PTHREAD_COND_INITIALIZER { { 0, 0, 0, 0, 0, (void *) 0, 0, 0 } }

typedef union
{
  struct
  {
    int __lock;
    unsigned int __futex;
    __extension__ unsigned long long int __total_seq;
    __extension__ unsigned long long int __wakeup_seq;
    __extension__ unsigned long long int __woken_seq;
    void *__mutex;
    unsigned int __nwaiters;
    unsigned int __broadcast_seq;
  } __data;
  char __size[__SIZEOF_PTHREAD_COND_T]; ///__SIZEOF_PTHREAD_COND_T 48
  __extension__ long long int __align;
} pthread_cond_t;

atomic.c

  • atomic_compare_and_exchange_val_acq(mem, newval, oldval) 如果,mem的数值等于oldval, 则mem=newval
  • atomic_compare_and_exchange_bool_acq(mem, newval, oldval) 如果,mem的数值等于oldval, 则mem=newval,返回为0
/* We have by default no support for atomic operations.  So define
   them non-atomic.  If this is a problem somebody will have to come
   up with real definitions.  */

/* The only basic operation needed is compare and exchange.  */
#define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \
  ({ __typeof (mem) __gmemp = (mem);				      \
     __typeof (*mem) __gret = *__gmemp;				      \
     __typeof (*mem) __gnewval = (newval);			      \
								      \
     if (__gret == (oldval))					      \
       *__gmemp = __gnewval;					      \
     __gret; })

#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \
  ({ __typeof (mem) __gmemp = (mem);				      \
     __typeof (*mem) __gnewval = (newval);			      \
								      \
     *__gmemp == (oldval) ? (*__gmemp = __gnewval, 0) : 1; })

pthread_cond_wait.c

1. 首先加条件变量内部的自旋锁
2. 释放外部的互斥量
3. __total_seq自增,__futex自增,__nwaiters+=1 << COND_NWAITERS_SHIFT,因为_nwaiters有个掩码
/* We have one new user of the condvar.  */
++cond->__data.__total_seq;
++cond->__data.__futex;
cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT;
4. do{}while (val == seq || cond->__data.__woken_seq == val);val == seq防止被虚假唤醒,由于__futex的改变导致被唤醒,如果有多个消费者处在wait端,signal会唤醒一次,多核CPU中可能会唤醒多次。应用层需要进行再次判断条件是否满足。cond->__data.__woken_seq == val表示有多个线程被唤醒,当一个线程执行了 
++cond->__data.__woken_seq后cond->__data.__woken_seq == val成立,下一个线程就不会执行。
struct _condvar_cleanup_buffer
{
  int oldtype;
  pthread_cond_t *cond;
  pthread_mutex_t *mutex;
  unsigned int bc_seq;
};

void
__attribute__ ((visibility ("hidden")))
__condvar_cleanup (void *arg)
{
  struct _condvar_cleanup_buffer *cbuffer =
    (struct _condvar_cleanup_buffer *) arg;
  unsigned int destroying;
  int pshared = (cbuffer->cond->__data.__mutex == (void *) ~0l)
		? LLL_SHARED : LLL_PRIVATE;

  /* We are going to modify shared data.  */
  lll_lock (cbuffer->cond->__data.__lock, pshared);

  if (cbuffer->bc_seq == cbuffer->cond->__data.__broadcast_seq)
    {
      /* This thread is not waiting anymore.  Adjust the sequence counters
	 appropriately.  We do not increment WAKEUP_SEQ if this would
	 bump it over the value of TOTAL_SEQ.  This can happen if a thread
	 was woken and then canceled.  */
      if (cbuffer->cond->__data.__wakeup_seq
	  < cbuffer->cond->__data.__total_seq)
	{
	  ++cbuffer->cond->__data.__wakeup_seq;
	  ++cbuffer->cond->__data.__futex;
	}
      ++cbuffer->cond->__data.__woken_seq;
    }

  cbuffer->cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT;

  /* If pthread_cond_destroy was called on this variable already,
     notify the pthread_cond_destroy caller all waiters have left
     and it can be successfully destroyed.  */
  destroying = 0;
  if (cbuffer->cond->__data.__total_seq == -1ULL
      && cbuffer->cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT))
    {
      lll_futex_wake (&cbuffer->cond->__data.__nwaiters, 1, pshared);
      destroying = 1;
    }

  /* We are done.  */
  lll_unlock (cbuffer->cond->__data.__lock, pshared);

  /* Wake everybody to make sure no condvar signal gets lost.  */
  if (! destroying)
    lll_futex_wake (&cbuffer->cond->__data.__futex, INT_MAX, pshared);

  /* Get the mutex before returning unless asynchronous cancellation
     is in effect.  */
  __pthread_mutex_cond_lock (cbuffer->mutex);
}

int __pthread_cond_wait (cond, mutex)
     pthread_cond_t *cond;
     pthread_mutex_t *mutex;
{
  struct _pthread_cleanup_buffer buffer;
  struct _condvar_cleanup_buffer cbuffer;
  int err;
  int pshared = (cond->__data.__mutex == (void *) ~0l)
		? LLL_SHARED : LLL_PRIVATE;
  /*
   Values for 'private' parameter of locking macros.  Yes, the
   definition seems to be backwards.  But it is not.  The bit will be
   reversed before passing to the system call.  
   #define LLL_PRIVATE	0
   #define LLL_SHARED	FUTEX_PRIVATE_FLAG  //128
   */
  LIBC_PROBE (cond_wait, 2, cond, mutex);

  /* Make sure we are alone.  */
  lll_lock (cond->__data.__lock, pshared);

  /* Now we can release the mutex.  */
  err = __pthread_mutex_unlock_usercnt (mutex, 0);
  if (__builtin_expect (err, 0))
    {
      lll_unlock (cond->__data.__lock, pshared);
      return err;
    }

  /* We have one new user of the condvar.  */
  ++cond->__data.__total_seq;
  ++cond->__data.__futex;
  cond->__data.__nwaiters += 1 << COND_NWAITERS_SHIFT;

  /* Remember the mutex we are using here.  If there is already a
     different address store this is a bad user bug.  Do not store
     anything for pshared condvars.  */
  if (cond->__data.__mutex != (void *) ~0l)
    cond->__data.__mutex = mutex;

  /* Prepare structure passed to cancellation handler.  */
  cbuffer.cond = cond;
  cbuffer.mutex = mutex;

  /* Before we block we enable cancellation.  Therefore we have to
     install a cancellation handler.  */
  __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);

  /* The current values of the wakeup counter.  The "woken" counter
     must exceed this value.  */
  unsigned long long int val;
  unsigned long long int seq;
  val = seq = cond->__data.__wakeup_seq;
  /* Remember the broadcast counter.  */
  cbuffer.bc_seq = cond->__data.__broadcast_seq;

  do
    {
      unsigned int futex_val = cond->__data.__futex;

      /* Prepare to wait.  Release the condvar futex.  */
      lll_unlock (cond->__data.__lock, pshared);

      /* Enable asynchronous cancellation.  Required by the standard.  */
      cbuffer.oldtype = __pthread_enable_asynccancel ();

      /* Wait until woken by signal or broadcast.  */
      lll_futex_wait (&cond->__data.__futex, futex_val, pshared);

      /* Disable asynchronous cancellation.  */
      __pthread_disable_asynccancel (cbuffer.oldtype);

      /* We are going to look at shared data again, so get the lock.  */
      lll_lock (cond->__data.__lock, pshared);

      /* If a broadcast happened, we are done.  */
      if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
	goto bc_out;

      /* Check whether we are eligible for wakeup.  */
      val = cond->__data.__wakeup_seq;
    }
  while (val == seq || cond->__data.__woken_seq == val);

  /* Another thread woken up.  */
  ++cond->__data.__woken_seq;

 bc_out:

  cond->__data.__nwaiters -= 1 << COND_NWAITERS_SHIFT;

  /* If pthread_cond_destroy was called on this varaible already,
     notify the pthread_cond_destroy caller all waiters have left
     and it can be successfully destroyed.  */
  if (cond->__data.__total_seq == -1ULL
      && cond->__data.__nwaiters < (1 << COND_NWAITERS_SHIFT))
    lll_futex_wake (&cond->__data.__nwaiters, 1, pshared);

  /* We are done with the condvar.  */
  lll_unlock (cond->__data.__lock, pshared);

  /* The cancellation handling is back to normal, remove the handler.  */
  __pthread_cleanup_pop (&buffer, 0);

  /* Get the mutex before returning.  */
  return __pthread_mutex_cond_lock (mutex);
}

pthread_cond_signal.c

int
__pthread_cond_signal (cond)
     pthread_cond_t *cond;
{
  int pshared = (cond->__data.__mutex == (void *) ~0l)
		? LLL_SHARED : LLL_PRIVATE;

  LIBC_PROBE (cond_signal, 1, cond);

  /* Make sure we are alone.  */
  /// 加锁
  lll_lock (cond->__data.__lock, pshared);

  /* Are there any waiters to be woken?  */
  /// 判断消费者有人在wait
  if (cond->__data.__total_seq > cond->__data.__wakeup_seq)
    {
      /* Yes.  Mark one of them as woken.  */
      /// 自增唤醒序列的长度
      ++cond->__data.__wakeup_seq;
      /// 自增__futex
      ++cond->__data.__futex;

      /* Wake one.  */
      if (! __builtin_expect (lll_futex_wake_unlock (&cond->__data.__futex, 1,
						     1, &cond->__data.__lock,
						     pshared), 0))
	return 0;
      /// 唤醒1一个&cond->__data.__futex
      lll_futex_wake (&cond->__data.__futex, 1, pshared);
    }

  /* We are done.  */
  /// 解锁
  lll_unlock (cond->__data.__lock, pshared);

  return 0;
}

pthread_cond_broadcast.c

int
__pthread_cond_broadcast (cond)
     pthread_cond_t *cond;
{
  LIBC_PROBE (cond_broadcast, 1, cond);

  int pshared = (cond->__data.__mutex == (void *) ~0l)
		? LLL_SHARED : LLL_PRIVATE;
  /* Make sure we are alone.  */
  lll_lock (cond->__data.__lock, pshared);

  /* Are there any waiters to be woken?  */
  if (cond->__data.__total_seq > cond->__data.__wakeup_seq)
    {
      /* Yes.  Mark them all as woken.  */
      cond->__data.__wakeup_seq = cond->__data.__total_seq;
      cond->__data.__woken_seq = cond->__data.__total_seq;
      cond->__data.__futex = (unsigned int) cond->__data.__total_seq * 2;
      int futex_val = cond->__data.__futex;
      /* Signal that a broadcast happened.  */
      ++cond->__data.__broadcast_seq;

      /* We are done.  */
      lll_unlock (cond->__data.__lock, pshared);

      /* Do not use requeue for pshared condvars.  */
      if (cond->__data.__mutex == (void *) ~0l)
	goto wake_all;

      /* Wake everybody.  */
      pthread_mutex_t *mut = (pthread_mutex_t *) cond->__data.__mutex;

      /* XXX: Kernel so far doesn't support requeue to PI futex.  */
      /* XXX: Kernel so far can only requeue to the same type of futex,
	 in this case private (we don't requeue for pshared condvars).  */
      if (__builtin_expect (mut->__data.__kind
			    & (PTHREAD_MUTEX_PRIO_INHERIT_NP
			       | PTHREAD_MUTEX_PSHARED_BIT), 0))
	goto wake_all;

      /* lll_futex_requeue returns 0 for success and non-zero
	 for errors.  */
      if (__builtin_expect (lll_futex_requeue (&cond->__data.__futex, 1,
					       INT_MAX, &mut->__data.__lock,
					       futex_val, LLL_PRIVATE), 0))
	{
	  /* The requeue functionality is not available.  */
	wake_all:
      /// 唤醒INT_MAX个人
	  lll_futex_wake (&cond->__data.__futex, INT_MAX, pshared);
	}

      /* That's all.  */
      return 0;
    }

  /* We are done.  */
  lll_unlock (cond->__data.__lock, pshared);

  return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[源码解读]深入理解pthread中的condition条件变量 的相关文章

  • 无法在 QGLWidget 中设置所需的 OpenGL 版本

    我正在尝试在 Qt 4 8 2 中使用 QGLWidget 我注意到 QGLWidget 创建的默认上下文不显示 OpenGL 3 1 以上的任何输出 Qt wiki 有一个教程 http qt project org wiki How t
  • 与 MinGW 的静态和动态/共享链接

    我想从一个简单的链接用法开始来解释我的问题 假设有一个图书馆z它可以编译为共享库 libz dll D libs z shared libz dll 或静态库 libz a D libs z static libz a 让我想要链接它 然后
  • C++中类成员函数相互调用有什么好处?

    我是 C 新手 我发现下面的编程风格对我来说很有趣 我在这里写了一个简化版本 include
  • 如何以编程方式删除受信任的根证书颁发机构中的证书?

    我需要能够从组织中的每台电脑中删除特定的证书 是的 我可以逐个座位 但我要到周四才能完成 而且我没有人力逐个座位 是否有使用 C 的编程方式来执行此操作 我认为你不需要编写任何 C 看看certmgr exe del http msdn m
  • 如何在 C# 中以编程方式将行添加到 DataGrid?

    正如标题所述 我正在尝试使用 C 以编程方式将行添加到 DataGrid 但我似乎无法使其工作 这是我到目前为止所拥有的 I have a DataGrid declared as dg in the XAML foreach string
  • C# 结构默认值

    我有一个方法 它接受一个包含许多具有基本数据类型的字段的结构 我想传递大部分默认值 但需要进行一些调整 但我了解结构声明中的基本字段不能包含默认值声明 例如struct S int a 42 现在是这样的 OptionsStruct opt
  • 公交车公共交通算法

    我正在开发一个可以查找公交路线的离线 C 应用程序 我可以提取时间表 巴士 路线数据 我正在寻找适用于基本数据的最简单的解决方案 可以使用什么算法来查找从巴士站 A 到巴士站 B 的路线 是否有适用于 C Java 的开源解决方案 数据库的
  • 自己绘制的WPF自定义滑块

    这是我关于堆栈溢出的第一个问题 所以不要踢它 我在尝试创建 Mac 风格的滑块控件时遇到问题 我已经发现这个解决方案 http www codeproject com KB miscctrl MAC Slider aspx我已经在我的解决方
  • 重载算术运算符

    赋值运算符可以声明为 T 运算符 const t 在类中 但不能以这种方式定义算术运算符 它必须是友元函数 我不明白为什么 你能解释一下吗 算术运算符不必须是友元 那么你可以这样定义 MyClass MyClass operator con
  • 如何在 C 中链接目标文件?失败并显示“架构 x86_64 的未定义符号”

    因此 我尝试在我的文件 file2 c 中使用另一个 C file1 c 文件中定义的函数 为了做到这一点 我包含了 file1 file1 h 的标头 但是 每当我尝试使用 gcc 编译文件时 我都会收到以下错误 Undefined sy
  • ASP.NET - Crystal Report Viewer 打印按钮在 ASP.NET 中不起作用

    我正在使用 Visual Studio 2008 但我遇到了水晶报告问题 当我单击打印按钮时 它会将我带到弹出窗口 但未找到页面 弹出的网址是 http localhost aspnet client System Web 2 0 5072
  • DateTime.ParseExact - 为什么 yy 变成 2015 而不是 1915

    为什么 NET 假定以下年份是 2015 年 而不是 1915 年 var d DateTime ParseExact 20 11 15 dd MM yy new CultureInfo en GB 我想 它会尝试接近 但其背后是否有合理的
  • 如何从 Powerpoint 2010 导出电影?

    如何使用 MS Office PIA 主互操作程序集 或其他方式以编程方式将嵌入视频从 powerpoint 2010 导出到外部文件 在演示文稿中嵌入视频是 Powerpoint 2010 中的一项新功能 我找不到解决方案 PPTX 文件
  • main.cpp 是必需的吗?

    我试图编译一个程序cmake 我最终删除了我的main cpp文件 我刚刚将其复合到另一个包含我的项目名称的文件中 即 我刚刚将主函数剪切并粘贴到该文件中 问题是我有一个main cpp未发现错误 不确定是否在C 一个名为main cpp是
  • fgets溢出后如何清除输入缓冲区?

    当输入字符串超出其预定义限制时 我遇到了 fgets 的小问题 以下面的例子为例 for index 0 index lt max index printf Enter the d string index 1 if fgets input
  • MPI - 发送和接收列

    我需要从一个进程发送矩阵列并从另一个进程接收它 我尝试运行以下程序 但得到了一个奇怪的结果 至少我这么认为 仅复制矩阵的第一个元素 某些矩阵元素会发生意外变化 include
  • 在一个解决方案中调用不同项目的方法

    1 个解决方案中有 3 个项目 我对第一个项目中的主文件进行的主要操作 但是我需要调用第三个项目中的方法并使用类 例如 第三个项目有 public DataClasses1DataContext base global WindowsFor
  • 如何防止 Lotus Notes 用户转发或复制通过 System.Net.Mail 发送的邮件?

    我想使用 SMTP 客户端 uiing microsft net 以 C 作为编程语言发送电子邮件 但是对于通过SMTP客户端发送的电子邮件 我们是否可以添加 禁止转发 或 禁止复制 等安全功能 我不希望电子邮件的收件人转发或复制电子邮件的
  • 跟踪白色背景中的白球(Python/OpenCV)

    我在 Python 3 中使用 OpenCV 来检测白场上的白 黑球 并给出它的精确 x y 半径 和颜色 我使用函数 cv2 Canny 和 cv2 findContours 来找到它 但问题是 cv2 Canny 并不总是检测到圆的完整
  • 使用通用存储库模式和流畅的 nHibernate

    我目前正在开发一个中型应用程序 它将访问不同站点上的 2 个或更多 SQL 数据库等 我正在考虑使用类似的东西 http mikehadlow blogspot com 2008 03 using irepository pattern w

随机推荐

  • Linux中nginx如何重启、启动与停止/设置开机自启动

    一 启动 usr local nginx sbin nginx c usr local nginx conf nginx conf 启动代码格式 nginx安装目录地址 c nginx配置文件地址 例如 root localhost usr
  • Unity3D性能优化——工具篇

    性能优化是游戏项目开发中一个重要且必须的元素 用户和项目的需求在并且会持续增长 而即便在硬件设备高速发展的今天 游戏特效 画质 场景复杂度的需求也都向着榨干硬件性能的趋势提升 无论研发团队有多么丰富的经验积累 性能优化永远是一个非常棘手而又
  • 操作系统理论知识9

    我的操作系统笔记 第四章 存储器管理 存储器部件 主存 保存进程运行时的程序和数据 寄存器 速度最快 价格昂贵容量不大 一般以字为单位 只要存放指令一次操作的数据就够了 高速缓存 速度快 存放部分内存数据 硬件自动处理 磁盘缓存 内存的一部
  • Quartz概述

    Quartz是开源任务调度框架中的翘楚 它提供了强大的 任务调度机制 Quartz允许开发人员灵活的定义触发器的调度时间表 并可对触发器和任务进行关联映射 此外 Quartz提供了调度运行环境的持久化机制 可以保存并恢复调度现场 即使系统因
  • Photoscan/Metashape 2.0.0中的地面激光扫描处理

    在Metashape 原Photoscan 2 0 0 结构化地面激光扫描和非结构化航空激光扫描都可以使用导入点云 文件 gt 导入 gt 导入点云 命令导入 导入时会保留所有点属性 包括结构化信息 本文讨论以下主题 如何将激光扫描数据导入
  • 创建进程函数fork的使用

    1 pid t fork void 作用 创建一个新的进程 返回值 如果调用成功 返回两次 返回值为0 代表当前进程是子进程 返回值为非负数 代表当前进程为父进程 调用失败 返回 1 C程序一开始 就会产生一个进程 当这个进程执行到fork
  • 力扣刷题序号459.重复的子字符串——C语言实现

    给定一个非空的字符串 s 检查是否可以通过由它的一个子串重复多次构成 思路与算法 根本思路 区间移动 1 先判断一定不是由子串构成的情况 即输入的母串长度为0或1的情况 2 当母串长度 gt 2时 需考虑多种情况 需枚举算法 先遍历整个母串
  • 关于css中的z-index 属性

    检索或设置对象的层叠顺序 较大 number 值的对象会覆盖在较小 number 值的对象之上 如两个绝对定位对象的此属性具有同样的 number 值 那么将依据它们在HTML文档中声明的顺序层叠 对于未指定此属性的绝对定位对象 此属性的
  • ios弱网测试_弱网测试方法整理

    背景 昨天和几个同事讨论弱网测试方法 发现并不是很多人在没有公司专门开发的弱网工具的前提下 知道如何去进行弱网的模拟测试 于是就整理了以下几种测试方法 供大家参考 下面只是对弱网设置的界面进行了简单的介绍 有兴趣的童鞋可以深入研究 另外还有
  • win10 git 命令行出现 no matching host key type found. Their offer: ssh-rsa 解决方案

    一 现象 win10电脑 配置好公私钥之后 仍然无法直接用 git ssh的方式 下载代码 出现形如 no matching host key type found Their offer ssh rsa 的错误 转载 https www
  • CSS动画——加载的菊花转动画

    CSS动画 加载的菊花转动画 最近在整理工作过程中用到的一些动画 菊花转loading就是其中一个 本人比较爱较劲 看到这个就想用代码实现 虽然我很菜 但是我也要做一个菜中VIP 话不多说 先插播一个类似想要实现的gif吧 对不起这个gif
  • scrapy效率提升篇

    scrapy基于twisted异步IO框架 downloader是多线程的 但是 由于python使用GIL 全局解释器锁 保证同时只有一个线程在使用解释器 这极大限制了并行性 在处理运算密集型程序的时候 Python的多线程效果很差 而如
  • vue+wangEditor的富文本编辑器的使用

    vue wangEditor的富文本编辑器的使用 先配置新建一个
  • 华为防火墙配置了限制一台主机只能访问固定域名和IP的安全策略后打开网站加载速度很慢半天打不开

    环景 华为USG6311E VRP Software Version 5 170 USG6300E V600R007C00SPC200 V200R007C00SPC091 PC联想win10专业版 谷歌浏览器版本 88 0 4324 182
  • SVM —— 在复杂数据上应用核函数

    对于非线性可分的数据 我们需要使用一种称为核函数 kernel 的工具将数据转换成易于分类器理解的形式 目录 利用核函数将数据映射到高位空间 径向基核函数 利用核函数将数据映射到高位空间 对于非线性可分的数据 我们要将数据从一个特征空间转换
  • docker-compose部署elk

    version 3 4 services es master container name es master image elasticsearch 7 3 1 restart always ports 19200 9200 19300
  • MySQL数据库8(十四)聚合函数和运算符

    MySQL数据库8 十四 聚合函数和运算符 利用一些统计函数 聚合函数 count 统计每组中的数量 如果统计的目标是字段 那么不统计空NULL字段 如果为 代表统计记录 avg 求平均值 sum 求和 max 求最大值 min 求在最小值
  • 【游戏提取/超详细记录向】关于unity游戏的资源提取所需资源及方法简介(AssetBundle及libil2cpp.so等解密)

    方法一 AssetStudio提取 仅针对ab包及 assets boundle和 unity3d未加密的情况下 1 在我们拿到一个游戏的安装包时 首先会疑惑如何打开 下载bandzipBandizip Free zip 7z unzip
  • WIN10搭建FTP(全套完整)

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net zhj 1121 article details 85344185 目录 已经搭建好了
  • [源码解读]深入理解pthread中的condition条件变量

    深入理解pthread中的condition条件变量 文章目录 深入理解pthread中的condition条件变量 pthread cond init c pthread cond wait c pthread cond signal c